bluetoothappprofiles/avrcp/remconbeareravrcp/src/avrcpoutgoingcommandhandler.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 
       
    17 
       
    18 /**
       
    19  @file
       
    20  @internalComponent
       
    21  @released
       
    22 */
       
    23 
       
    24 #include <remconcoreapi.h>
       
    25 
       
    26 #include "avcpanel.h"
       
    27 #include "controlcommand.h"
       
    28 #include "avrcpoutgoingcommandhandler.h"
       
    29 #include "avrcplog.h"
       
    30 #include "avrcprouter.h"
       
    31 #include "avrcptimer.h"
       
    32 #include "avrcputils.h"
       
    33 #include "controlbearer.h"
       
    34 
       
    35 //---------------------------------------------------------------------
       
    36 // Construction/Destruction
       
    37 //---------------------------------------------------------------------
       
    38 
       
    39 /** Factory function.
       
    40 
       
    41 @param aBearer	The CRemConBearerAvrcp this is to handle commands for.
       
    42 @param aObserver The observer of the bearer. Used to aquire converters.
       
    43 @param aRouter	A CRcpRouter to use for communication with remote devices.
       
    44 @param aTimer	CDeltaTimer to use for queuing timed events.
       
    45 @return A fully constructed CRcpOutgoingCommandHandler.
       
    46 @leave System wide error codes.
       
    47 */
       
    48 CRcpOutgoingCommandHandler* CRcpOutgoingCommandHandler::NewL(MRemConControlCommandInterface& aCommandInterface, 
       
    49 	MRemConBearerObserver& aObserver,
       
    50 	CRcpRouter& aRouter,
       
    51 	CDeltaTimer& aTimer)
       
    52 	{
       
    53 	LOG_STATIC_FUNC
       
    54 	CRcpOutgoingCommandHandler* handler = new(ELeave)CRcpOutgoingCommandHandler(aCommandInterface, aObserver, aRouter, aTimer);
       
    55 	return handler;
       
    56 	}
       
    57 
       
    58 /** Constructor.
       
    59 
       
    60 @param aBearer	The CRemConBearerAvrcp this is to handle commands for.
       
    61 @param aObserver The observer of the bearer. Used to aquire converters.
       
    62 @param aRouter	A CRcpRouter to use for communication with remote devices.
       
    63 @param aTimer	CDeltaTimer to use for queuing timed events.
       
    64 @return A partially constructed CRcpIncomingCommandHandler.
       
    65 @leave System wide error codes.
       
    66 */		
       
    67 CRcpOutgoingCommandHandler::CRcpOutgoingCommandHandler(MRemConControlCommandInterface& aCommandInterface, 
       
    68 	MRemConBearerObserver& aObserver,
       
    69 	CRcpRouter& aRouter,
       
    70 	CDeltaTimer& aTimer) : iCommandQueue(_FOFF(CControlCommand, iHandlingLink)),
       
    71 	iNotifyCommandQueue(_FOFF(CControlCommand, iHandlingLink)),
       
    72 	iCommandInterface(aCommandInterface), iObserver(aObserver), iRouter(aRouter), iTimer(aTimer)
       
    73 	{
       
    74 	LOG_FUNC
       
    75 	}
       
    76 
       
    77 /** Destructor.
       
    78 */
       
    79 CRcpOutgoingCommandHandler::~CRcpOutgoingCommandHandler()
       
    80 	{
       
    81 	LOG_FUNC
       
    82 
       
    83 	ClearQueue(iCommandQueue);
       
    84 	ClearQueue(iNotifyCommandQueue);
       
    85 	}	
       
    86 
       
    87 void CRcpOutgoingCommandHandler::ClearQueue(TDblQue<CControlCommand>& aQue)
       
    88 	{
       
    89 	while(!aQue.IsEmpty())
       
    90 		{
       
    91 		CControlCommand* command = aQue.First();
       
    92 		command->CancelTimer(iTimer);
       
    93 		command->iHandlingLink.Deque();
       
    94 		command->DecrementUsers();
       
    95 		}
       
    96 	}
       
    97 //---------------------------------------------------------------------
       
    98 // Called from the bearer
       
    99 //---------------------------------------------------------------------
       
   100 
       
   101 /** Tell the handler to gracefully shutdown.
       
   102 
       
   103 @param aClearQueue Whether to clear the queue without handling the things 
       
   104 					on it.  If this is true the commands will be deleted.
       
   105 					If this is false then pending commands will have responses
       
   106 					generated to RemCon.
       
   107 */
       
   108 void CRcpOutgoingCommandHandler::Disconnect(TBool aClearQueue)
       
   109 	{
       
   110 	LOG_FUNC	
       
   111 	ProcessDisconnect(iCommandQueue, aClearQueue);
       
   112 	ProcessDisconnect(iNotifyCommandQueue, aClearQueue);
       
   113 	}
       
   114 
       
   115 void CRcpOutgoingCommandHandler::ProcessDisconnect(TDblQue<CControlCommand>& aQue, TBool aClearQueue)
       
   116 	{
       
   117 	while(!aQue.IsEmpty())
       
   118 		{
       
   119 		CControlCommand* command = aQue.First();
       
   120 		iRouter.RemoveFromSendQueue(*command);
       
   121 		command->CancelTimer(iTimer);
       
   122 		
       
   123 		if(aClearQueue)
       
   124 			{
       
   125 			GenerateFailureResult(*command, KErrDisconnected);
       
   126 			}
       
   127 
       
   128 		command->iHandlingLink.Deque();
       
   129 		command->DecrementUsers();
       
   130 		}
       
   131 	}
       
   132 /** Sends a new command.
       
   133 
       
   134 @param aInterfaceUid	The RemCon client interface this command is from.
       
   135 @param aCommand			The operation id within aInterfaceUid.
       
   136 @param aId				A unique identifier provided by RemCon.
       
   137 @param aCommandData		Data associated with this command.
       
   138 @param aAddr			Bluetooth address of device to send this command to.
       
   139 @leave KErrNoMemory or system wide error code.
       
   140 @leave Command parsing error.
       
   141 */
       
   142 void CRcpOutgoingCommandHandler::SendCommandL(TUid aInterfaceUid, 
       
   143 		TUint aCommand, 
       
   144 		TUint aId,  
       
   145 		RBuf8& aCommandData, 
       
   146 		const TBTDevAddr& aAddr)
       
   147 	{
       
   148 	LOG_FUNC
       
   149 	
       
   150 	if(aInterfaceUid.iUid == KRemConCoreApiUid)
       
   151 		{
       
   152 		// Passthrough commands are stateful, so we need to examine the
       
   153 		// history - we can't just blindly wham it on the queue.
       
   154 		HandleCoreApiCommandL(aCommand, aId, aCommandData, aAddr);
       
   155 		}
       
   156 	else
       
   157 		{
       
   158 		SendCommandL(aInterfaceUid, aCommand, aId, aCommandData, EFalse, aAddr, ETrue, EFalse);
       
   159 		}
       
   160 	}
       
   161 
       
   162 /** Sends a new notify command.
       
   163 
       
   164 @param aInterfaceUid	The RemCon client interface this command is from.
       
   165 @param aCommand			The operation id within aInterfaceUid.
       
   166 @param aId				A unique identifier provided by RemCon, the transaction ID.
       
   167 @param aCommandData		Data associated with this command.
       
   168 @param aAddr			Bluetooth address of device to send this command to.
       
   169 @leave KErrNoMemory or system wide error code.
       
   170 @leave Command parsing error.
       
   171 */
       
   172 void CRcpOutgoingCommandHandler::SendNotifyCommandL(TUid aInterfaceUid, 
       
   173 		TUint aCommand, 
       
   174 		TUint aId,  
       
   175 		RBuf8& aCommandData, 
       
   176 		const TBTDevAddr& aAddr)
       
   177 	{
       
   178 	LOG_FUNC
       
   179 	SendCommandL(aInterfaceUid, aCommand, aId, aCommandData, EFalse, aAddr, ETrue, ETrue);
       
   180 	}
       
   181 	
       
   182 //---------------------------------------------------------------------
       
   183 // Data notifications from the router
       
   184 //---------------------------------------------------------------------
       
   185 
       
   186 /** Called by the router to provide a new response.
       
   187 
       
   188 @param aFrame The AV/C frame for this response. Ownership is taken.
       
   189 @param aTransLabel The AVCTP transaction id of this response. This is used
       
   190 				to match it with its command.
       
   191 @param aAddr The remote from which this response originated
       
   192 */
       
   193 void CRcpOutgoingCommandHandler::ReceiveResponse(const TDesC8& aMessageInformation, 
       
   194 	SymbianAvctp::TTransactionLabel aTransLabel, 
       
   195 	TBool aIpidBitSet)
       
   196 	{
       
   197 	LOG_FUNC
       
   198 	
       
   199 	CAVCFrame* frame = NULL;
       
   200 	TInt err = KErrNone;
       
   201 	if(!aIpidBitSet)
       
   202 		{
       
   203 		TRAP(err, frame = CAVCFrame::NewL(aMessageInformation, AVC::EResponse));
       
   204 		}
       
   205 	
       
   206 	if(!err)
       
   207 		{
       
   208 		CControlCommand* command = NULL;
       
   209 		command = FindInQueue(iCommandQueue, aTransLabel);
       
   210 		if ( command != NULL )
       
   211 			{
       
   212 			//Found, so it is a normal command response.
       
   213 			ProcessReceiveResponse(frame, aIpidBitSet, command, EFalse);
       
   214 			}
       
   215 		else
       
   216 			{
       
   217 			//Try to find in the notify command queue.
       
   218 			command = FindInQueue(iNotifyCommandQueue, aTransLabel);
       
   219 			if( command != NULL )
       
   220 			    {
       
   221 			    //Found, so it is a notify command response.
       
   222 			    ProcessReceiveResponse(frame, aIpidBitSet, command, ETrue);
       
   223 			    }
       
   224 			}
       
   225 		
       
   226 		delete frame;
       
   227 		}
       
   228 	}
       
   229 
       
   230 CControlCommand* CRcpOutgoingCommandHandler::FindInQueue(TDblQue<CControlCommand>& aQue, 
       
   231 		SymbianAvctp::TTransactionLabel aTransLabel)
       
   232 	{	
       
   233 	CControlCommand* command = NULL;
       
   234 	TDblQueIter<CControlCommand> iter(aQue);
       
   235 	while (iter)
       
   236 		{
       
   237 		command = iter++;
       
   238 		if(command->TransactionLabel() == aTransLabel)
       
   239 			{
       
   240 			return command;
       
   241 			}
       
   242 		}
       
   243 	
       
   244 	return NULL;
       
   245 	}
       
   246 
       
   247 void CRcpOutgoingCommandHandler::ProcessReceiveResponse(CAVCFrame* aFrame, 
       
   248 		TBool aIpidBitSet,
       
   249 		CControlCommand* aCommand, 
       
   250 		TBool aNotify)
       
   251 	{
       
   252 	aCommand->CancelTimer(iTimer);
       
   253 	
       
   254 	TInt err = KErrNone;
       
   255 	// Inform the bearer if this is something it knows about
       
   256 	// ie not a click release
       
   257 	if(aCommand->KnownToBearer())
       
   258 		{
       
   259 		if(!aIpidBitSet)
       
   260 			{
       
   261 			if(aFrame->Data().Length() < KAVCFrameHeaderLength)
       
   262 				{
       
   263 				// Drop corrupt frames
       
   264 				return;
       
   265 				}
       
   266 
       
   267 			err = aCommand->ParseIncomingResponse(iObserver, *aFrame);
       
   268 			}
       
   269 		else
       
   270 			{
       
   271 			// If aIpidBitSet is true that means AVRCP is not supported
       
   272 			// by the remote end.  We handle this in the same way as not
       
   273 			// supported commands, passing them up to RemCon as not 
       
   274 			// supported, so just map the ctype here, rather than setting
       
   275 			// up another path for ipid handling, but we need pass as the
       
   276 			// frame the original because we don't get one from AVCTP if
       
   277 			// ipid is set.
       
   278 			aCommand->SetResponseType(KErrNotSupported);
       
   279 			err = aCommand->ParseIncomingResponse(iObserver, aCommand->Frame());
       
   280 			}
       
   281 		
       
   282 		if ( aNotify )
       
   283 		    {//This is a notify command
       
   284 		    iCommandInterface.MrccciNewNotifyResponse(*aCommand);
       
   285 		    }
       
   286 		else
       
   287 			{
       
   288 			iCommandInterface.MrccciNewResponse(*aCommand);
       
   289 			}
       
   290 		}
       
   291 
       
   292 	TBool doDeque = ETrue;
       
   293 	if ( (!aIpidBitSet) && (err == KErrNone) && (aNotify) && (aFrame->Type() == AVC::EInterim))
       
   294 		{
       
   295 		doDeque = EFalse;
       
   296 		}
       
   297 	
       
   298 	// If this a passthrough press that hasn't yet been released, we need 
       
   299 	// to wait for a release before getting rid of this, otherwise we're done.
       
   300 	if(aCommand == iUnreleasedCommand)
       
   301 		{
       
   302 		iUnreleasedHasResponse = ETrue;
       
   303 		StartReleaseTimer(*iUnreleasedCommand);
       
   304 		doDeque = EFalse;
       
   305 		}
       
   306 	
       
   307 	if ( doDeque )
       
   308 		{
       
   309 		aCommand->iHandlingLink.Deque();
       
   310 		aCommand->DecrementUsers();
       
   311 		}
       
   312 	}
       
   313 /** Called by the router to complete a send.
       
   314 
       
   315 @param aCommand The command which has been sent.
       
   316 @param aSendResult The result of the send. KErrNone if successful.
       
   317 */	
       
   318 void CRcpOutgoingCommandHandler::MessageSent(CAvrcpCommand& aCommand, TInt aSendResult)
       
   319 	{
       
   320 	LOG_FUNC
       
   321 	
       
   322 	if(aSendResult == KErrNone)
       
   323 		{
       
   324 		// Set off response timer
       
   325 		StartResponseTimer(static_cast<CControlCommand&>(aCommand));
       
   326 		}
       
   327 	else
       
   328 		{
       
   329 		CControlCommand* command = FindInQueue(iNotifyCommandQueue, aCommand.TransactionLabel());
       
   330 		
       
   331 		if(command)
       
   332 			{
       
   333 			command->SetNotifyVolumeChangeResult(command->Frame());
       
   334 			iCommandInterface.MrccciNewNotifyResponse(*command);
       
   335 			}
       
   336 		else
       
   337 			{
       
   338 			command = FindInQueue(iCommandQueue, aCommand.TransactionLabel());
       
   339 			
       
   340 			// Generate error response up to RemCon
       
   341 			// if this is a core command we can set the result, 
       
   342 			// otherwise we just return it as we got it.
       
   343 			if(command->Frame().Opcode() == AVC::EPassThrough)
       
   344 				{
       
   345 				// Need to insert before setting the button action so we have
       
   346 				// long enough data
       
   347 				if (!command->InsertCoreResult(aSendResult))
       
   348 					{
       
   349 					if(command->Click())
       
   350 						{
       
   351 						command->SetCoreButtonAction(ERemConCoreApiButtonClick, ETrue);
       
   352 						}
       
   353 					}
       
   354 				}
       
   355 			
       
   356 			iCommandInterface.MrccciNewResponse(*command);
       
   357 			}
       
   358 		
       
   359 		command->iHandlingLink.Deque();
       
   360 		command->DecrementUsers();
       
   361 		}
       
   362 	}
       
   363 
       
   364 //---------------------------------------------------------------------
       
   365 // Internal Utility functions
       
   366 //---------------------------------------------------------------------
       
   367 
       
   368 void CRcpOutgoingCommandHandler::CleanupUnreleased()
       
   369 	{
       
   370 	iUnreleasedCommand->CancelTimer(iTimer);
       
   371 	iUnreleasedCommand->iHandlingLink.Deque();
       
   372 	iUnreleasedCommand->DecrementUsers();
       
   373 	iUnreleasedHasResponse = EFalse;
       
   374 	}
       
   375 	
       
   376 /** Handle a command that is part of the Core API.
       
   377 
       
   378 @param aCommand			The operation id within aInterfaceUid.
       
   379 @param aId				A unique identifier provided by RemCon.
       
   380 @param aCommandData		Data associated with this command.
       
   381 @param aAddr			Bluetooth address of device to send this command to.
       
   382 @leave KErrNoMemory or system wide error code.
       
   383 @leave Command parsing error.
       
   384 */
       
   385 void CRcpOutgoingCommandHandler::HandleCoreApiCommandL(TUint aCommand, 
       
   386 		TUint aId,  
       
   387 		RBuf8& aCommandData, 
       
   388 		const TBTDevAddr& aAddr)
       
   389 	{
       
   390 	if(aCommandData.Length() < KRemConCoreApiButtonDataLength)
       
   391    		{
       
   392    		User::Leave(KErrCorrupt);
       
   393    		}
       
   394 
       
   395 	TInt buttonData;
       
   396 	AvrcpUtils::ReadCommandDataToInt(aCommandData, 
       
   397    		KRemConCoreApiButtonDataOffset, KRemConCoreApiButtonDataLength, buttonData);
       
   398  
       
   399  	// First check if there's anything we need to do before sending this command,
       
   400  	// mainly releasing a previous press.  	
       
   401 	if(iUnreleasedCommand)
       
   402 		{
       
   403 		TUint prevOpId = iUnreleasedCommand->RemConOperationId();
       
   404 		
       
   405 		if(aCommand == prevOpId)
       
   406 			{
       
   407 			// Either we've received a release, or we've refreshed the press.
       
   408 			// If the unreleased press has already been responded too we can
       
   409 			// dispose of it now.  
       
   410 			// If the unreleased press has not been responded too we can 
       
   411 			// treat it like a normal command on reception of response, so just
       
   412 			// set iUnreleased to NULL.
       
   413 			if(iUnreleasedHasResponse)
       
   414 				{
       
   415 				CleanupUnreleased();
       
   416 				}
       
   417 
       
   418 			iUnreleasedCommand = NULL;
       
   419 			}
       
   420 		else
       
   421 			{
       
   422 			// A new operation!
       
   423 			if(buttonData != ERemConCoreApiButtonRelease)
       
   424 				{
       
   425 				// Try and generate the release for the previous command, if
       
   426 				// if fails then the remote will just have to assume it.
       
   427 				// There's no point leaving this to the release timer, because
       
   428 				// we want to send another command now, so even if we send the
       
   429 				// release later the remote should ignore it.
       
   430 				TRAP_IGNORE(GenerateCommandL(*iUnreleasedCommand, ERemConCoreApiButtonRelease));
       
   431 				
       
   432 				if(iUnreleasedHasResponse)
       
   433 					{
       
   434 					CleanupUnreleased();
       
   435 					}
       
   436 
       
   437 				iUnreleasedCommand = NULL;
       
   438 				}
       
   439 			else
       
   440 				{
       
   441 				// A release for a command other than iUnreleased.  We can't 
       
   442 				// send this now.
       
   443 				User::Leave(KErrNotReady);
       
   444 				}
       
   445 			}
       
   446 		}
       
   447 	else if(buttonData == ERemConCoreApiButtonRelease)
       
   448 		{
       
   449 		// We don't have an unreleased command.  We must have already 
       
   450 		// released this, either via the timer, or because we've sent
       
   451 		// another command in the meantime.  We can't send this now.
       
   452 		// Leaving synchronously means we don't need to worry about generating
       
   453 		// a fake response, which may mislead the application.
       
   454 		User::Leave(KErrNotReady);
       
   455 		}
       
   456    	
       
   457    	if(buttonData == ERemConCoreApiButtonClick)
       
   458    		{	
       
   459 		// aCommandData is still owned by RemCon until we return successfully.
       
   460 		// If we try the operations with the new data first we won't end up 
       
   461 		// in a situation where the new CControlCommand thinks that it owns 
       
   462 		// aCommandData, then the release operation leaves, so RemCon also
       
   463 		// thinks it owns aCommandData.
       
   464 		
       
   465 		RBuf8 pressBuf;
       
   466 		pressBuf.CreateL(aCommandData);
       
   467 		CleanupClosePushL(pressBuf);
       
   468 		
       
   469 		AvrcpUtils::SetCommandDataFromInt(pressBuf, 
       
   470 			KRemConCoreApiButtonDataOffset, KRemConCoreApiButtonDataLength, ERemConCoreApiButtonPress);
       
   471 		SendCommandL(TUid::Uid(KRemConCoreApiUid), aCommand, aId, pressBuf, ETrue, aAddr, ETrue, EFalse);
       
   472 		
       
   473 		// Data has been taken ownership of by SendCommandL, so can just let 
       
   474 		// pressbuf go out of scope.
       
   475 		CleanupStack::Pop(&pressBuf);
       
   476 		
       
   477 		AvrcpUtils::SetCommandDataFromInt(aCommandData, 
       
   478 			KRemConCoreApiButtonDataOffset, KRemConCoreApiButtonDataLength, ERemConCoreApiButtonRelease);
       
   479 		SendCommandL(TUid::Uid(KRemConCoreApiUid), aCommand, aId, aCommandData, ETrue, aAddr, EFalse, EFalse);
       
   480 		}
       
   481 	else if(buttonData == ERemConCoreApiButtonPress)
       
   482 		{
       
   483 		iUnreleasedCommand = &SendCommandL(TUid::Uid(KRemConCoreApiUid), aCommand, aId, aCommandData, EFalse, aAddr, ETrue, EFalse);
       
   484 		iReleaseTimerExpiryCount = 0;
       
   485 		}
       
   486 	else
       
   487 		{
       
   488 		// Must be release
       
   489 		__ASSERT_DEBUG(buttonData == ERemConCoreApiButtonRelease, AvrcpUtils::Panic(EAvrcpUnknownButtonAction));
       
   490 		SendCommandL(TUid::Uid(KRemConCoreApiUid), aCommand, aId, aCommandData, EFalse, aAddr, ETrue, EFalse);
       
   491 		}
       
   492 	}
       
   493 
       
   494 /** Creates a command from the RemCon data.
       
   495 
       
   496 This is an internal utility function.
       
   497 
       
   498 A CControlCommand will be created.  Calling ProcessOutgoingCommandL on
       
   499 this creates a CAVCFrame from the provided data.  If an AV/C frame
       
   500 can be created the command will be added to the outgoing queue to
       
   501 wait a response from the remote.  Otherwise this function will
       
   502 leave.
       
   503 
       
   504 @param aInterfaceUid	The RemCon client interface this command is from.
       
   505 @param aCommand			The operation id within aInterfaceUid.
       
   506 @param aId				A unique identifier provided by RemCon.
       
   507 @param aCommandData		Data associated with this command.
       
   508 @param aIsClick			Whether this is a button click.
       
   509 @param aAddr			Bluetooth address of device to send this command to.
       
   510 @return The generated command.
       
   511 @leave KErrNoMemory or system wide error code.
       
   512 @leave Command parsing error.
       
   513 */
       
   514 CControlCommand& CRcpOutgoingCommandHandler::SendCommandL(TUid aInterfaceUid,
       
   515 	TUint aCommand, 
       
   516 	TUint aId, 
       
   517 	RBuf8& aCommandData, 
       
   518 	TBool aIsClick, 
       
   519 	const TBTDevAddr& aAddr,
       
   520 	TBool aKnownToBearer,
       
   521 	TBool aNotify)
       
   522 	{
       
   523 	LOG_FUNC
       
   524 	// Create a command and wham it on our queue, so we can match it up with its response
       
   525 	// CControlCommand::NewL takes ownership of the data in aCommandData then NULLs aCommandData
       
   526 	// so a leave later in the function won't cause double deletion.
       
   527 	CControlCommand* command = CControlCommand::NewL(aInterfaceUid, aCommand, aId, iCurrentTrans, 
       
   528 			aCommandData, aIsClick, aAddr, aKnownToBearer);
       
   529 	CleanupStack::PushL(command);	
       
   530 	
       
   531 	command->ProcessOutgoingCommandL(iObserver);
       
   532 	CleanupStack::Pop(command);
       
   533 	command->IncrementUsers();
       
   534 	
       
   535 	if ( aNotify )
       
   536 		{
       
   537 		iNotifyCommandQueue.AddLast(*command);
       
   538 		}
       
   539 	else
       
   540 		{
       
   541 		iCommandQueue.AddLast(*command);
       
   542 		}
       
   543 	
       
   544 	// Increment our transaction id
       
   545 	iCurrentTrans = (iCurrentTrans + 1) % SymbianAvctp::KMaxTransactionLabel;
       
   546 	
       
   547 	// Command stays on the queue till we've got the response
       
   548 	iRouter.AddToSendQueue(*command);
       
   549 	
       
   550 	return *command;
       
   551 	}
       
   552 
       
   553 /** Generate a failure response to RemCon.
       
   554 
       
   555 This sets the result for a passthrough command.
       
   556 It informs the bearer of the new response.
       
   557 
       
   558 @param aCommand The command to finish off.
       
   559 @param aResult The result (only valid for passthrough)
       
   560 */
       
   561 void CRcpOutgoingCommandHandler::GenerateFailureResult(CControlCommand& aCommand, TInt aResult)
       
   562 	{
       
   563 	// Response is only necessary if the bearer knows about this command.
       
   564 	if(aCommand.KnownToBearer() && (aCommand.Frame().Opcode() == AVC::EPassThrough))
       
   565 		{
       
   566 		if (aCommand.InsertCoreResult(aResult) == KErrNone)
       
   567 			{
       
   568 			if(aCommand.Click())
       
   569 				{
       
   570 				aCommand.SetCoreButtonAction(ERemConCoreApiButtonClick, ETrue);
       
   571 				}
       
   572 			else if(aCommand.ButtonAct() == AVCPanel::EButtonPress)
       
   573 				{
       
   574 				aCommand.SetCoreButtonAction(ERemConCoreApiButtonPress, ETrue);
       
   575 				}
       
   576 			else
       
   577 				{
       
   578 				aCommand.SetCoreButtonAction(ERemConCoreApiButtonRelease, ETrue);
       
   579 				}
       
   580 
       
   581 			iCommandInterface.MrccciNewResponse(aCommand);
       
   582            }
       
   583 		}	
       
   584 	}
       
   585 	
       
   586 /** Generate a command to the remote.
       
   587 
       
   588 This is needed in situations where the application has not met the avrcp
       
   589 button refresh requirements so we need to internally generate something
       
   590 to stop the remote getting a bad impression of us.
       
   591 
       
   592 @param aCommand The command to be issue again.
       
   593 */
       
   594 void CRcpOutgoingCommandHandler::GenerateCommandL(CControlCommand& aCommand, TRemConCoreApiButtonAction aButtonAct)
       
   595 	{
       
   596 	LOG_FUNC
       
   597 	
       
   598 	RBuf8 commandData;
       
   599 	commandData.CreateMaxL(KRemConCoreApiButtonDataLength);
       
   600 	
       
   601 	AvrcpUtils::SetCommandDataFromInt(commandData, 
       
   602 		KRemConCoreApiButtonDataOffset, KRemConCoreApiButtonDataLength, aButtonAct);
       
   603 
       
   604 	// This will not leave before taking ownership of commandData.
       
   605 	SendCommandL(aCommand.RemConInterfaceUid(), aCommand.RemConOperationId(), aCommand.RemConCommandId(), commandData, EFalse, 
       
   606 		aCommand.RemoteAddress(), EFalse, EFalse);
       
   607 	}
       
   608 
       
   609 //------------------------------------------------------------------------------------
       
   610 // Timer functions
       
   611 //------------------------------------------------------------------------------------
       
   612 
       
   613 /** Starts the response timer.
       
   614 
       
   615 AVRCP mandates a remote respond within 100ms of receiving a command.
       
   616 This is the timer for that, and is started when a command has 
       
   617 successfully been sent.
       
   618 
       
   619 @param aCommand The command to start the timer for.
       
   620 */		
       
   621 void CRcpOutgoingCommandHandler::StartResponseTimer(CControlCommand& aCommand)
       
   622 	{
       
   623 	LOG_FUNC
       
   624 	// These use placement new, so cannot fail
       
   625 	TAvrcpTimerExpiryInfo* timerInfo = new(aCommand.TimerExpiryInfo())TAvrcpTimerExpiryInfo(this, aCommand);
       
   626 
       
   627 	TCallBack callback(ResponseExpiry, timerInfo);
       
   628 	TDeltaTimerEntry* timerEntry = new(aCommand.TimerEntry())TDeltaTimerEntry(callback);
       
   629 	
       
   630 	iTimer.Queue(KRcpResponseTimeOut, *timerEntry);
       
   631 	}
       
   632 	
       
   633 /** Callback when response timer expires.
       
   634 
       
   635 This is a static forwarding function.
       
   636 
       
   637 @param aExpiryInfo The information used by the real ResponseExpiry to
       
   638 					deal with the timer expiry.
       
   639 */
       
   640 TInt CRcpOutgoingCommandHandler::ResponseExpiry(TAny* aExpiryInfo)
       
   641 	{
       
   642 	LOG_STATIC_FUNC
       
   643 	TAvrcpTimerExpiryInfo *timerInfo = reinterpret_cast<TAvrcpTimerExpiryInfo*>(aExpiryInfo);
       
   644 	(reinterpret_cast<CRcpOutgoingCommandHandler*>(timerInfo->iHandler))->ResponseExpiry(timerInfo->iCommand);
       
   645 	
       
   646 	return KErrNone;
       
   647 	}
       
   648 	
       
   649 /** Deals with response timeout.
       
   650 
       
   651 This sends a timeout response to RemCon.
       
   652 
       
   653 @param aCommand	The CControlCommand that has expired.
       
   654 */
       
   655 void CRcpOutgoingCommandHandler::ResponseExpiry(CControlCommand& aCommand)
       
   656 	{
       
   657 	LOG_FUNC
       
   658 	
       
   659 	GenerateFailureResult(aCommand, KErrTimedOut);	
       
   660 	
       
   661 	// Failed to get a response to this, don't bother about trying
       
   662 	// to release it.
       
   663 	if(iUnreleasedCommand == &aCommand)
       
   664 		{
       
   665 		iUnreleasedCommand = NULL;
       
   666 		__ASSERT_DEBUG(!iUnreleasedHasResponse, AvrcpUtils::Panic(EAvrcpPressHasPhantomResponse));
       
   667 		}
       
   668 
       
   669 	aCommand.iHandlingLink.Deque();
       
   670 	aCommand.DecrementUsers();
       
   671 	}
       
   672 
       
   673 /** Starts the release timer.
       
   674 
       
   675 AVRCP requires a press to be refreshed less than 2s after the first
       
   676 press, if it is not to be assumed to have been released.  We pass 
       
   677 this requirement on to RemCon clients as we don't know when they might
       
   678 go away and we don't want to keep buttons pressed forever.  If the 
       
   679 release timer expires we will assume a release, and generate it to
       
   680 the remote.
       
   681 
       
   682 @param aCommand The command to start the timer for.
       
   683 */	
       
   684 void CRcpOutgoingCommandHandler::StartReleaseTimer(CControlCommand& aCommand)
       
   685 	{
       
   686 	LOG_FUNC
       
   687 
       
   688 	// These cannot fail as we use placement new
       
   689 	TAvrcpTimerExpiryInfo* timerInfo = new(aCommand.TimerExpiryInfo())TAvrcpTimerExpiryInfo(this, aCommand);
       
   690 		
       
   691 	TCallBack callback(ReleaseExpiry, timerInfo);
       
   692 	TDeltaTimerEntry* timerEntry = new(aCommand.TimerEntry())TDeltaTimerEntry(callback);
       
   693 
       
   694 	iTimer.Queue(KRcpOutgoingButtonReleaseTimeout, *timerEntry);
       
   695 	}
       
   696 
       
   697 /** Callback when release timer expires.
       
   698 
       
   699 This is a static forwarding function.
       
   700 
       
   701 @param aExpiryInfo The information used by the real ReleaseExpiry to
       
   702 					deal with the timer expiry.
       
   703 */	
       
   704 TInt CRcpOutgoingCommandHandler::ReleaseExpiry(TAny* aExpiryInfo)
       
   705 	{
       
   706 	LOG_STATIC_FUNC
       
   707 	TAvrcpTimerExpiryInfo *timerInfo = reinterpret_cast<TAvrcpTimerExpiryInfo*>(aExpiryInfo);
       
   708 	(reinterpret_cast<CRcpOutgoingCommandHandler*>(timerInfo->iHandler))->ReleaseExpiry(timerInfo->iCommand);
       
   709 	
       
   710 	return KErrNone;
       
   711 	}
       
   712 
       
   713 /** Deals with expiry of release timer.
       
   714 
       
   715 1) Generate release for this command.
       
   716 2) Send release to remote.
       
   717 
       
   718 @param aCommand	The CControlCommand that has expired.
       
   719 */
       
   720 void CRcpOutgoingCommandHandler::ReleaseExpiry(CControlCommand& aCommand)
       
   721 	{
       
   722 	LOG_FUNC
       
   723 	__ASSERT_DEBUG((aCommand.ButtonAct() == AVCPanel::EButtonPress), AvrcpUtils::Panic(EAvrcpReleaseExpiryForRelease));
       
   724 	__ASSERT_DEBUG(&aCommand == iUnreleasedCommand, AvrcpUtils::Panic(EAvrcpReleaseExpiryForOldCommand));
       
   725 	__ASSERT_DEBUG(iUnreleasedHasResponse, AvrcpUtils::Panic(EAvrcpReleaseTimerStartedWithoutResponse));
       
   726 		
       
   727 	iReleaseTimerExpiryCount++;
       
   728 	
       
   729 	TBool commandCompleted = ETrue;
       
   730 	// If the client is not yet obliged to refresh this, then send another press.  Otherwise generate
       
   731 	// the release for them.
       
   732 	if((iReleaseTimerExpiryCount * KRcpOutgoingButtonReleaseTimeout) < KRemConCoreApiPressRefreshInterval)
       
   733 		{
       
   734 		// This will try and generate a press that is identical to the original
       
   735 		// aCommand, but with a new AVCTP transaction id.
       
   736 		TRAPD(err, GenerateCommandL(aCommand, ERemConCoreApiButtonPress));
       
   737 		
       
   738 		if(!err)
       
   739 			{
       
   740 			// Start the timer again on the original command
       
   741 			StartReleaseTimer(aCommand);
       
   742 			commandCompleted = EFalse;
       
   743 			}
       
   744 		}
       
   745 	else
       
   746 		{
       
   747 		// Try an generate a release, but if it fails we just have to let the 
       
   748 		// remote assume it.
       
   749 		TRAP_IGNORE(GenerateCommandL(aCommand, ERemConCoreApiButtonRelease));
       
   750 		}
       
   751 		
       
   752 	// This condition may be true because
       
   753 	// -  we have failed to generate a press, in which case the remote is entitled 
       
   754 	//    to assume this is released, so we just give up on it.
       
   755 	// or
       
   756 	// -  the client has not met its press refresh obligation (whether we've 
       
   757 	//    successfully generated a release or not.
       
   758 	// In either case we won't do anything more with this command. 
       
   759 	if(commandCompleted)
       
   760 		{				
       
   761 		aCommand.iHandlingLink.Deque();
       
   762 		aCommand.DecrementUsers();
       
   763 		
       
   764 		iUnreleasedCommand = NULL;
       
   765 		iUnreleasedHasResponse = EFalse;
       
   766 		iReleaseTimerExpiryCount = 0;
       
   767 		}
       
   768 	}