bluetoothappprofiles/avrcp/remconbeareravrcp/src/avrcputils.cpp
changeset 70 f5508c13dfe0
parent 67 16e4b9007960
child 71 083fd884d7dd
equal deleted inserted replaced
67:16e4b9007960 70:f5508c13dfe0
     1 // Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 //
       
    15 
       
    16 #include <bt_sock.h>
       
    17 #include <e32base.h>
       
    18 #include <e32msgqueue.h>
       
    19 #include <remconaddress.h>
       
    20 #include <remconbeareravrcp.h>
       
    21 #include "avrcplog.h"
       
    22 #include "avrcputils.h"
       
    23 
       
    24 /**
       
    25 @file
       
    26 @internalComponent
       
    27 @released
       
    28 */
       
    29 
       
    30 /** Utility AVRCP panic function.
       
    31 
       
    32 @param aPanic The panic number.
       
    33 */
       
    34 void AvrcpUtils::Panic(TAvrcpPanic aPanic)
       
    35 	{
       
    36 	User::Panic(KAvrcpPanicName, aPanic);
       
    37 	}
       
    38 	
       
    39 
       
    40 /** Set the command data.  This overwrites the current
       
    41 contents of the data buffer.
       
    42 
       
    43 @param aCommandData The buffer in which to set the data.
       
    44 @param aOffset The offset within aCommandData to set the data.
       
    45 @param aLength The length of data to replace.
       
    46 @param aValue The new value for the replaced data.
       
    47 */
       
    48 void AvrcpUtils::SetCommandDataFromInt(RBuf8& aCommandData, 
       
    49 	TInt aOffset, TInt aLength, TInt aValue)
       
    50 	{
       
    51 	LOG_STATIC_FUNC
       
    52 	__ASSERT_DEBUG(aLength <= 4, Panic(EAvrcpCommandDataTooLong));
       
    53 	
       
    54 	for(TInt i = 0; i < aLength; i++)
       
    55 		{
       
    56 		aCommandData[aOffset+i] = aValue >> (8*i);
       
    57 		}
       
    58 	}
       
    59 
       
    60 /** Reads command data from the buffer to an int.
       
    61 
       
    62 @param aCommandData The buffer from which to read the data.
       
    63 @param aOffset The offset within aCommandData read from.
       
    64 @param aLength The length of data to read.  This must not be
       
    65 				more than 4.
       
    66 @param aValue On return, the value of the specified data section.
       
    67 */	
       
    68 void AvrcpUtils::ReadCommandDataToInt(const RBuf8& aCommandData, 
       
    69 	TInt aOffset, TInt aLength, TInt& aValue)
       
    70 	{
       
    71 	LOG_STATIC_FUNC
       
    72 	__ASSERT_DEBUG(aLength <= 4, Panic(EAvrcpCommandDataTooLong));
       
    73 	
       
    74 	aValue = 0;
       
    75 	
       
    76 	for(TInt i = 0 ; i < aLength; i++)
       
    77 		{
       
    78 		aValue |= aCommandData[aOffset+i]<<(8*i);
       
    79 		}
       
    80 	}
       
    81 
       
    82 /** Convert from a RemCon address to a bluetooth device address.
       
    83 
       
    84 @param aRemoteAddress The RemCon format address to convert.
       
    85 @param aBTAddr On return, the bluetooth device address.
       
    86 @return Whether the conversion could be performed successfully.
       
    87 */	
       
    88 TInt AvrcpUtils::RemConToBTAddr(const TRemConAddress& aRemoteAddress, TBTDevAddr& aBTAddr)
       
    89 	{
       
    90 	LOG_STATIC_FUNC
       
    91 	TInt err = KErrArgument;
       
    92 	
       
    93 	// Check client has provided us a valid address
       
    94 	if(aRemoteAddress.Addr().Length() == KBTDevAddrSize)
       
    95 		{
       
    96 		aBTAddr = TBTDevAddr(aRemoteAddress.Addr());
       
    97 		err = KErrNone;
       
    98 		}
       
    99 	else
       
   100 		{
       
   101 		__ASSERT_DEBUG(EFalse, AvrcpUtils::Panic(EAvrcpBadBTAddr));
       
   102 		}
       
   103 		
       
   104 	return err;
       
   105 	}
       
   106 
       
   107 /** Convert from a bluetooth device address to a RemCon address.
       
   108 
       
   109 We assume this cannot fail, as bluetooth addresses are generated
       
   110 internally rather than by a client, so they should always be
       
   111 valid, and so convertible.
       
   112 
       
   113 @param aBTAddr The bluetooth device address to convert.
       
   114 @param aRemoteAddress On return, the RemCon format address.
       
   115 */	
       
   116 void AvrcpUtils::BTToRemConAddr(const TBTDevAddr& aBTAddr, TRemConAddress& aRemConAddress)
       
   117 	{
       
   118 	LOG_STATIC_FUNC
       
   119 	aRemConAddress.Addr() = aBTAddr.Des();
       
   120 	aRemConAddress.BearerUid() = TUid::Uid(KRemConBearerAvrcpImplementationUid);
       
   121 	}
       
   122 
       
   123 NONSHARABLE_CLASS(CSpecificThreadCallBackBody)
       
   124 	: public CActive
       
   125 	{
       
   126 public:
       
   127 	static CSpecificThreadCallBackBody* NewL(const TCallBack& aCallBack, TInt aPriority);
       
   128 	~CSpecificThreadCallBackBody();
       
   129 	
       
   130 	TInt Start();
       
   131 	TInt CallBack();
       
   132 	void HandleCancel();
       
   133 	
       
   134 private:
       
   135 	CSpecificThreadCallBackBody(const TCallBack& aCallBack, TInt aPriority);
       
   136 	void ConstructL();
       
   137 	
       
   138 	TInt AsyncMessage(TInt aParam);
       
   139 	
       
   140 private: // from CActive
       
   141 	void RunL();
       
   142 	void DoCancel();
       
   143 	
       
   144 private:
       
   145 	TCallBack		iCallBack;
       
   146 	
       
   147 	RThread			iLocalThread;
       
   148 	
       
   149 	RMsgQueue<TInt>	iInbound;
       
   150 	RMsgQueue<TInt>	iOutbound;
       
   151 	};
       
   152 
       
   153 RSpecificThreadCallBack::RSpecificThreadCallBack()
       
   154 	: iBody(NULL)
       
   155 	{
       
   156 	LOG_FUNC
       
   157 	}
       
   158 
       
   159 TInt RSpecificThreadCallBack::Create(const TCallBack& aCallBack, TInt aPriority)
       
   160 	{
       
   161 	TRAPD(err, iBody = CSpecificThreadCallBackBody::NewL(aCallBack, aPriority));
       
   162 	return err;
       
   163 	}
       
   164 
       
   165 void RSpecificThreadCallBack::Close()
       
   166 	{
       
   167 	LOG_FUNC
       
   168 	delete iBody;
       
   169 	iBody = NULL;
       
   170 	}
       
   171 
       
   172 TInt RSpecificThreadCallBack::Start()
       
   173 	{
       
   174 	return iBody->Start();
       
   175 	}
       
   176 
       
   177 TInt RSpecificThreadCallBack::CallBack()
       
   178 	{
       
   179 	return iBody->CallBack();
       
   180 	}
       
   181 
       
   182 void RSpecificThreadCallBack::Cancel()
       
   183 	{
       
   184 	return iBody->HandleCancel();
       
   185 	}
       
   186 
       
   187 CSpecificThreadCallBackBody* CSpecificThreadCallBackBody::NewL(const TCallBack& aCallBack, TInt aPriority)
       
   188 	{
       
   189 	CSpecificThreadCallBackBody* self = new(ELeave) CSpecificThreadCallBackBody(aCallBack, aPriority);
       
   190 	CleanupStack::PushL(self);
       
   191 	self->ConstructL();
       
   192 	CleanupStack::Pop(self);
       
   193 	return self;
       
   194 	}
       
   195 
       
   196 CSpecificThreadCallBackBody::CSpecificThreadCallBackBody(const TCallBack& aCallBack, TInt aPriority)
       
   197 	: CActive(aPriority)
       
   198 	, iCallBack(aCallBack)
       
   199 	{
       
   200 	LOG_FUNC
       
   201 	}
       
   202 
       
   203 void CSpecificThreadCallBackBody::ConstructL()
       
   204 	{
       
   205 	User::LeaveIfError(iInbound.CreateLocal(1));
       
   206 	User::LeaveIfError(iOutbound.CreateLocal(1));
       
   207 	}
       
   208 
       
   209 CSpecificThreadCallBackBody::~CSpecificThreadCallBackBody()
       
   210 	{
       
   211 	LOG_FUNC
       
   212 	HandleCancel();
       
   213 	iInbound.Close();
       
   214 	iOutbound.Close();
       
   215 	iLocalThread.Close();
       
   216 	}
       
   217 
       
   218 TInt CSpecificThreadCallBackBody::Start()
       
   219 	{
       
   220 	TInt err = KErrNone;
       
   221 	if(!IsAdded())
       
   222 		{
       
   223 		err = iLocalThread.Duplicate(RThread());
       
   224 		if(err == KErrNone)
       
   225 			{
       
   226 			CActiveScheduler::Add(this);
       
   227 			iInbound.NotifyDataAvailable(iStatus);
       
   228 			SetActive();
       
   229 			}
       
   230 		}
       
   231 	return err;
       
   232 	}
       
   233 
       
   234 TInt CSpecificThreadCallBackBody::CallBack()
       
   235 	{
       
   236 	TInt err = KErrUnknown;
       
   237 	if(iLocalThread.Id() == RThread().Id())
       
   238 		{
       
   239 		// Simple synchronous case.
       
   240 		err = iCallBack.CallBack();
       
   241 		}
       
   242 	else
       
   243 		{
       
   244 		RThread thisThread;
       
   245 		err = thisThread.Duplicate(RThread());
       
   246 		if(err == KErrNone)
       
   247 			{
       
   248 			err = AsyncMessage(thisThread.Handle());
       
   249 			}
       
   250 		}
       
   251 	return err;
       
   252 	}
       
   253 	
       
   254 TInt CSpecificThreadCallBackBody::AsyncMessage(TInt aParam)
       
   255 	{
       
   256 	TInt err = KErrNone;
       
   257 	TRequestStatus logonStatus;
       
   258 	iLocalThread.Logon(logonStatus);
       
   259 	if(logonStatus == KErrNoMemory)
       
   260 		{
       
   261 		// This seems kludgy, but I think it is the most reliable way.
       
   262 		User::WaitForRequest(logonStatus); // Ensure the all requests are correct...
       
   263 		err = KErrNoMemory;
       
   264 		}
       
   265 	else
       
   266 		{
       
   267 		iInbound.SendBlocking(aParam);
       
   268 		TRequestStatus status;
       
   269 		iOutbound.NotifyDataAvailable(status);
       
   270 		User::WaitForRequest(status, logonStatus);
       
   271 		if(status == KRequestPending)
       
   272 			{
       
   273 			// Remote thread is dead
       
   274 			iOutbound.CancelDataAvailable();
       
   275 			User::WaitForRequest(status);
       
   276 			err = KErrDied;
       
   277 			}
       
   278 		else
       
   279 			{
       
   280 			// Success (the thread may have subsequently died, but we are only concerned with this call).
       
   281 			iLocalThread.LogonCancel(logonStatus);
       
   282 			User::WaitForRequest(logonStatus);
       
   283 			err = status.Int();
       
   284 			if(err == KErrNone)
       
   285 				{
       
   286 				iOutbound.ReceiveBlocking(err);
       
   287 				}
       
   288 			}
       
   289 		}
       
   290 	return err;
       
   291 	}
       
   292 
       
   293 
       
   294 void CSpecificThreadCallBackBody::RunL()
       
   295 	{
       
   296 	TInt threadHandle;
       
   297 	iInbound.ReceiveBlocking(threadHandle);
       
   298 	if(threadHandle == 0)
       
   299 		{
       
   300 		// 0 is a cancel message
       
   301 		// therefore don't do anything
       
   302 		iOutbound.SendBlocking(KErrNone);
       
   303 		}
       
   304 	else
       
   305 		{
       
   306 		RThread remoteThread;
       
   307 		remoteThread.SetHandleNC(threadHandle);
       
   308 		
       
   309 		TInt result = iCallBack.CallBack();
       
   310 		
       
   311 		// There doesn't seem to be a safe way of handling when the other thread
       
   312 		// dies......
       
   313 		iOutbound.SendBlocking(result);
       
   314 		
       
   315 		remoteThread.Close();
       
   316 		
       
   317 		iInbound.NotifyDataAvailable(iStatus);
       
   318 		SetActive();
       
   319 		}
       
   320 	}
       
   321 
       
   322 void CSpecificThreadCallBackBody::DoCancel()
       
   323 	{
       
   324 	if(RThread().Id() == iLocalThread.Id())
       
   325 		{
       
   326 		iInbound.CancelDataAvailable();
       
   327 		}
       
   328 	else
       
   329 		{
       
   330 		// other thread cancelling - so just complete the
       
   331 		// request
       
   332 		TRequestStatus* status = &iStatus;
       
   333 		User::RequestComplete(status, KErrCancel);
       
   334 		}
       
   335 	}
       
   336 
       
   337 void CSpecificThreadCallBackBody::HandleCancel()
       
   338 	{
       
   339 	if(IsAdded())
       
   340 		{
       
   341 		if(RThread().Id() == iLocalThread.Id())
       
   342 			{
       
   343 			Cancel(); // synchronous cancel is fine in same thread...
       
   344 			}
       
   345 		else
       
   346 			{
       
   347 			// In a different thread - this is more interesting...
       
   348 			TInt err = AsyncMessage(0); // 0 is special as it means cancel.
       
   349 			if(err == KErrDied && IsActive())
       
   350 				{
       
   351 				// Remote thread has already died so we need to tidy up the
       
   352 				// active object ourselves.
       
   353 				Cancel();
       
   354 				}
       
   355 			}
       
   356 		}
       
   357 	// else shouldn't be active...
       
   358 	}
       
   359 
       
   360