bluetoothappprofiles/avrcp/remconbeareravrcp/src/passthroughhelper.cpp
changeset 70 f5508c13dfe0
parent 67 16e4b9007960
child 71 083fd884d7dd
equal deleted inserted replaced
67:16e4b9007960 70:f5508c13dfe0
     1 // Copyright (c) 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 "avrcplog.h"
       
    17 #include "avrcprouter.h"
       
    18 #include "avrcputils.h"
       
    19 #include "controlcommand.h"
       
    20 #include "passthroughhelper.h"
       
    21 #include "controlbearer.h"
       
    22 
       
    23 
       
    24 CPassthroughHelper* CPassthroughHelper::NewL(CRcpRouter& aRouter, MRemConControlCommandInterface& aCommandInterface, CDeltaTimer& aTimer)
       
    25 	{
       
    26 	CPassthroughHelper* helper = new(ELeave)CPassthroughHelper(aRouter, aCommandInterface, aTimer);
       
    27 	CleanupStack::PushL(helper);
       
    28 	helper->ConstructL();
       
    29 	CleanupStack::Pop(helper);
       
    30 	return helper;
       
    31 	}
       
    32 
       
    33 CPassthroughHelper::~CPassthroughHelper()
       
    34 	{
       
    35 	}
       
    36 	
       
    37 CPassthroughHelper::CPassthroughHelper(CRcpRouter& aRouter, MRemConControlCommandInterface& aCommandInterface, CDeltaTimer& aTimer)
       
    38 	: iRouter(aRouter)
       
    39 	, iCommandInterface(aCommandInterface)
       
    40 	, iTimer(aTimer)
       
    41 	{
       
    42 	}
       
    43 
       
    44 void CPassthroughHelper::ConstructL()
       
    45 	{
       
    46 	}
       
    47 
       
    48 void CPassthroughHelper::Disconnect()
       
    49 	{
       
    50 	LOG_FUNC
       
    51 	
       
    52 	// We handle the last command as a special case to ensure the presses and
       
    53 	// releases are balanced from a clients' point of view.
       
    54 	if(iPreviousPassthrough)
       
    55 		{
       
    56 		iRouter.RemoveFromSendQueue(*iPreviousPassthrough);
       
    57 		BalanceHandledCommand(*iPreviousPassthrough);
       
    58 		}
       
    59 	}
       
    60 
       
    61 /** Sends a response to the remote device.
       
    62 
       
    63 Because of the 100ms timeout for responses we send a response to
       
    64 passthrough commands as soon as we receive them.  This means the real
       
    65 response from RemCon is currently ignored.  A real response is
       
    66 only sent if this is a vendor dependent command.
       
    67 
       
    68 @param aCommand The command to respond to.
       
    69 @param aErr The result of handling the command.  KErrNone if successful.
       
    70 			KErrNotSupported if this operation is not supported.
       
    71 */		
       
    72 void CPassthroughHelper::Respond(CControlCommand& aCommand, TInt aErr)
       
    73 	{
       
    74 	LOG_FUNC
       
    75 	aCommand.SetResponseType(aErr);
       
    76 	iRouter.AddToSendQueue(aCommand);
       
    77 	}
       
    78 
       
    79 /** To be called on completion of command handling.
       
    80 
       
    81 This aggregates the handler's tidying up of a finished
       
    82 command.
       
    83 
       
    84 @param aCommand The command to tidy up.
       
    85 */	
       
    86 void CPassthroughHelper::HandledCommand(CControlCommand& aCommand)
       
    87 	{
       
    88 	LOG_FUNC
       
    89 
       
    90 	// If this was the previous passthrough reset it to NULL
       
    91 	if(iPreviousPassthrough == &aCommand)
       
    92 		{
       
    93 		iPreviousPassthrough = NULL;
       
    94 		}
       
    95 	
       
    96 	aCommand.CancelTimer(iTimer);
       
    97 	aCommand.DecrementUsers();
       
    98 	}
       
    99 
       
   100 /** Handles incoming passthrough command.
       
   101 
       
   102 How the command is handled is dependent on this and the previous command.
       
   103 
       
   104 Previous command	This command 	Behaviour
       
   105 Press_op1			Press_op1		discard
       
   106 Press_op1			Release_op1		press_op1 hold timer not expired - generate click_op1
       
   107 									press_op1 release timer not expired - generate release_op1
       
   108 Press_op1			Press_op2		generate release_op1, start hold timer for press_op2
       
   109 Press_op1			Release_op2		press_op1 hold timer not expired - generate click_op1, discard release_op2
       
   110 									press_op1 release timer not expired - generate release_op1, discard release_op2
       
   111 Release_op1			Press_op1		start hold timer for press_op1
       
   112 Release_op1			Release_op1		discard
       
   113 Release_op1			Press_op2		start hold timer for press_op2	
       
   114 Release_op1			Release_op2		discard
       
   115 
       
   116 @param aCommand The command to handle.
       
   117 @leave System wide error code.
       
   118 */
       
   119 void CPassthroughHelper::HandlePassthrough(CControlCommand& aCommand)
       
   120 	{
       
   121 	LOG_FUNC
       
   122 	
       
   123 	// We can't map Vendor Unique passthrough command to clicks as we do
       
   124 	// not know how the button action is conveyed to the API.  All we can
       
   125 	// do is try and ensure that we try to generate a release if one is 
       
   126 	// missing by asking the convert.  We also need to consider them as
       
   127 	// part of the passthrough history for how other passthrough commands
       
   128 	// should be interpreted.
       
   129 	TUint8 thisOpId;
       
   130 	aCommand.Frame().OperationId(thisOpId);
       
   131 	if(!aCommand.IsAvrcpPassthrough())
       
   132 		{
       
   133 		aCommand.SetClick(EFalse);
       
   134 		}
       
   135 	
       
   136 	// Behaviour depends on previous command
       
   137 	TUint8 prevOpId;
       
   138 	if(iPreviousPassthrough)
       
   139 		{
       
   140 		__ASSERT_ALWAYS(iPreviousPassthrough->ButtonAct() == AVCPanel::EButtonPress,
       
   141 	 		AvrcpUtils::Panic(EAvrcpPressNotPreviousPassthroughCommand));
       
   142 
       
   143 		iPreviousPassthrough->Frame().OperationId(prevOpId);
       
   144 		}
       
   145 		
       
   146 	if(aCommand.ButtonAct() == AVCPanel::EButtonPress)
       
   147 		{
       
   148 		// Respond now so we can decrement users of aCommand at will
       
   149 		Respond(aCommand, KErrNone);		
       
   150 			
       
   151 		if(iPreviousPassthrough)
       
   152 			{
       
   153 			// previous is press, current is press
       
   154 			if(prevOpId == thisOpId)
       
   155 				{
       
   156 				// prevCommand is for same opId as this one.  Drop this
       
   157 				// command and reset release timer for prevCommand.
       
   158 				aCommand.DecrementUsers();
       
   159 				
       
   160 				iPreviousPassthrough->CancelTimer(iTimer);
       
   161 				// If still flagged as a click then the user won't have been notified
       
   162 				// of the press yet - so do it now.
       
   163 				if(iPreviousPassthrough->Click())
       
   164 					{
       
   165 					iPreviousPassthrough->SetClick(EFalse);
       
   166 					iPreviousPassthrough->SetCoreButtonAction(ERemConCoreApiButtonPress, ETrue);
       
   167 					iCommandInterface.MrcciNewCommand(*iPreviousPassthrough, iPreviousPassthrough->ClientId());
       
   168 					}
       
   169 				StartReleaseTimer(*iPreviousPassthrough);
       
   170 				}
       
   171 			else if(iPreviousPassthrough->Click())
       
   172 				{
       
   173 				// prevCommand is click, Hold timer not expired.  Generate click
       
   174 				// to RemCon then start waiting for click.
       
   175 				iPreviousPassthrough->CancelTimer(iTimer);
       
   176 				iPreviousPassthrough->SetCoreButtonAction(ERemConCoreApiButtonClick, ETrue);
       
   177 				iCommandInterface.MrcciNewCommand(*iPreviousPassthrough, iPreviousPassthrough->ClientId());
       
   178 				
       
   179 				HandledCommand(*iPreviousPassthrough);
       
   180 									
       
   181 				// Start waiting for click (core api) or release (vendor unique)
       
   182 				NewPress(aCommand);
       
   183 				}
       
   184 			else
       
   185 				{
       
   186 				// prevCommand is not click, Release timer not expired.  Generate
       
   187 				// release to RemCon then start waiting for click.
       
   188 				BalanceHandledCommand(*iPreviousPassthrough);
       
   189 						
       
   190 				// Start waiting for click (core api) or release (vendor unique)
       
   191 				NewPress(aCommand);
       
   192 				}
       
   193 			}
       
   194 		else
       
   195 			{
       
   196 			// No previous command, current is press
       
   197 			
       
   198 			// Start waiting for click (core api) or release (vendor unique)
       
   199 			NewPress(aCommand);
       
   200 			}	
       
   201 		}
       
   202 	else if(iPreviousPassthrough)
       
   203 		{
       
   204 		// previous is press, current is release
       
   205 		if(prevOpId == thisOpId)
       
   206 			{
       
   207 			Respond(aCommand, KErrNone);
       
   208 					
       
   209 			if(iPreviousPassthrough->Click())
       
   210 				{
       
   211 				// Button release for same opId with hold timeout
       
   212 				aCommand.DecrementUsers(); // Drop this command.
       
   213 				
       
   214 				// Cancel hold timer and send the previous command as a click.
       
   215 				iPreviousPassthrough->CancelTimer(iTimer); 
       
   216 				iPreviousPassthrough->SetCoreButtonAction(ERemConCoreApiButtonClick, ETrue);
       
   217 				iCommandInterface.MrcciNewCommand(*iPreviousPassthrough, iPreviousPassthrough->ClientId());
       
   218 				
       
   219 				HandledCommand(*iPreviousPassthrough);
       
   220 				}
       
   221 			else
       
   222 				{
       
   223 				// Button release for same opId, hold timeout expired
       
   224 				iPreviousPassthrough->CancelTimer(iTimer);
       
   225 				HandledCommand(*iPreviousPassthrough);
       
   226 				
       
   227 				if(aCommand.IsAvrcpPassthrough())
       
   228 					{
       
   229 					aCommand.SetCoreButtonAction(ERemConCoreApiButtonRelease, ETrue);
       
   230 					}
       
   231 				iCommandInterface.MrcciNewCommand(aCommand, aCommand.ClientId());
       
   232 				aCommand.DecrementUsers();
       
   233 				}
       
   234 			}
       
   235 		else
       
   236 			{
       
   237 			// Drop this release and continue waiting for release to
       
   238 			// prevCommand.
       
   239 			Respond(aCommand, KErrNone);
       
   240 			aCommand.DecrementUsers();
       
   241 			}
       
   242 		}
       
   243 	else
       
   244 		{
       
   245 		// No prevCommand, this is a release
       
   246 		// To get here either:
       
   247 		// - this is a spurious release
       
   248 		// - we've given up waiting for this
       
   249 		
       
   250 		// Because responses must be made within 100ms we respond before waiting
       
   251 		// to see what RemCon thinks.
       
   252 		Respond(aCommand, KErrNone);
       
   253 		aCommand.DecrementUsers();
       
   254 		}
       
   255 	}
       
   256 
       
   257 void CPassthroughHelper::NewPress(CControlCommand& aCommand)
       
   258 	{
       
   259 	LOG_FUNC
       
   260 	__ASSERT_DEBUG(!iPreviousPassthrough, AVRCP_PANIC(EPreviousPassthroughNonNullReplacing));
       
   261 	
       
   262 	iPreviousPassthrough = &aCommand;
       
   263 	
       
   264 	if(aCommand.IsAvrcpPassthrough())
       
   265 		{
       
   266 		StartHoldTimer(aCommand);
       
   267 		}
       
   268 	else
       
   269 		{
       
   270 		iCommandInterface.MrcciNewCommand(aCommand, aCommand.ClientId());
       
   271 		StartReleaseTimer(aCommand);
       
   272 		}
       
   273 	}
       
   274 
       
   275 /** To be called on completion of command handling for an unbalanced press.
       
   276 
       
   277 This is a special version of the HandledCommand which which assumes the
       
   278 command has been notified to the user and so balances the notification
       
   279 of a press with a generated release command.
       
   280 
       
   281 @param aCommand The command to tidy up.
       
   282 */
       
   283 void CPassthroughHelper::BalanceHandledCommand(CControlCommand& aCommand)
       
   284 	{
       
   285 	LOG_FUNC
       
   286 	// CancelTimer also frees the timer entries, so needs to be called
       
   287 	// here even though there's no pending entry.
       
   288 	aCommand.CancelTimer(iTimer);
       
   289 	
       
   290 	// If this was the previous passthrough reset it to NULL
       
   291 	if(iPreviousPassthrough == &aCommand)
       
   292 		{
       
   293 		iPreviousPassthrough = NULL;
       
   294 		}
       
   295 	
       
   296 	// Here we handle the case that a press has been informed but a release has yet
       
   297 	// to be reported, and if it hasn't we provide the notification.
       
   298 	if(aCommand.ButtonAct() == AVCPanel::EButtonPress && !aCommand.Click())
       
   299 		{
       
   300 		if(aCommand.IsAvrcpPassthrough())
       
   301 			{
       
   302 			// FIXME I think we might need a new transaction id here?
       
   303 			TRAPD(err, aCommand.ReSetCoreButtonActionL(ERemConCoreApiButtonRelease, ETrue));
       
   304 			if (err == KErrNone)
       
   305 				{
       
   306 				iCommandInterface.MrcciNewCommand(aCommand, aCommand.ClientId());
       
   307 				}
       
   308 				else
       
   309 				{
       
   310 				// just silently drop this command, do not send command to remconserv
       
   311 				}
       
   312 			}
       
   313 		else
       
   314 			{
       
   315 			// Need see if we can get a valid release from the converter.  Pass
       
   316 			// it the same AVC Frame as the press, but with the button action 
       
   317 			// flipped to release.  If we can't do this then there's not a
       
   318 			// lot we can do.  
       
   319 			// FIXME
       
   320 			}
       
   321 		}
       
   322 	aCommand.iHandlingLink.Deque();
       
   323 	aCommand.DecrementUsers();
       
   324 	}
       
   325 
       
   326 
       
   327 //------------------------------------------------------------------------------------
       
   328 // Timer functions
       
   329 //------------------------------------------------------------------------------------
       
   330 
       
   331 /** Starts the hold timer (only applicable to press commands).
       
   332 
       
   333 This is the timer to determine whether a command will be passed to RemCon
       
   334 as a click.  If the timer expires before a matching release is received
       
   335 the press and release will be sent separately.  Otherwise,  a click is 
       
   336 sent.
       
   337 
       
   338 @param aCommand The command to start the timer for.
       
   339 */	
       
   340 void CPassthroughHelper::StartHoldTimer(CControlCommand& aCommand)
       
   341 	{
       
   342 	LOG_FUNC
       
   343 	
       
   344 	// These cannot fail as we use placement new	
       
   345 	TAvrcpTimerExpiryInfo* timerInfo = new(aCommand.TimerExpiryInfo())TAvrcpTimerExpiryInfo(this, aCommand);
       
   346 	
       
   347 	TCallBack callback(HoldExpiry, timerInfo);
       
   348 	TDeltaTimerEntry* timerEntry = new(aCommand.TimerEntry())TDeltaTimerEntry(callback);
       
   349 	
       
   350 	iTimer.Queue(KRcpHoldThreshold, *timerEntry);
       
   351 	}
       
   352 
       
   353 /** Callback when hold timer expires.
       
   354 
       
   355 This is a static forwarding function.
       
   356 
       
   357 @param aExpiryInfo The information used by the real HoldExpiry to
       
   358 					deal with the timer expiry.
       
   359 */	
       
   360 TInt CPassthroughHelper::HoldExpiry(TAny* aExpiryInfo)
       
   361 	{
       
   362 	LOG_STATIC_FUNC
       
   363 	TAvrcpTimerExpiryInfo *timerInfo = reinterpret_cast<TAvrcpTimerExpiryInfo*>(aExpiryInfo);
       
   364 	(reinterpret_cast<CPassthroughHelper*>(timerInfo->iHandler))->HoldExpiry(timerInfo->iCommand);
       
   365 	
       
   366 	return KErrNone;
       
   367 	}
       
   368 	
       
   369 /** Deals with expiry of hold timer.
       
   370 
       
   371 1) This is not a click.  Set click to false for this command.
       
   372 2) Inform RemCon of available press command
       
   373 3) Start release timer.
       
   374 
       
   375 @param aCommand	The CControlCommand that has expired.
       
   376 */
       
   377 void CPassthroughHelper::HoldExpiry(CControlCommand& aCommand)
       
   378 	{
       
   379 	LOG_FUNC
       
   380 	__ASSERT_DEBUG((aCommand.ButtonAct() == AVCPanel::EButtonPress), AvrcpUtils::Panic(EAvrcpHoldExpiryForRelease));
       
   381 	
       
   382 	// We haven't received a release soon enough to treat this as
       
   383 	// a click.  Send the press on to RemCon and wait for a release.
       
   384 	// CancelTimer also frees the timer entries, so needs to be called
       
   385 	// here even though there's no pending entry.
       
   386 	aCommand.CancelTimer(iTimer);
       
   387 	aCommand.SetClick(EFalse);
       
   388 	aCommand.SetCoreButtonAction(ERemConCoreApiButtonPress, ETrue);
       
   389 	iCommandInterface.MrcciNewCommand(aCommand, aCommand.ClientId());
       
   390 	StartReleaseTimer(aCommand);
       
   391 	}
       
   392 
       
   393 /** Starts the release timer (only applicable to press commands).
       
   394 
       
   395 If a release is not received quickly enough to treat this press as
       
   396 a click, the release timer is started.  2s after receiving a button
       
   397 press we should assume the release if we haven't received one.
       
   398 
       
   399 @param aCommand The command to start the timer for.
       
   400 */		
       
   401 void CPassthroughHelper::StartReleaseTimer(CControlCommand& aCommand)
       
   402 	{
       
   403 	TAvrcpTimerExpiryInfo* timerInfo = new(aCommand.TimerExpiryInfo())TAvrcpTimerExpiryInfo(this, aCommand);
       
   404 
       
   405 	TCallBack callback(ReleaseExpiry, timerInfo);
       
   406 	TDeltaTimerEntry* timerEntry = new(aCommand.TimerEntry())TDeltaTimerEntry(callback);
       
   407 	
       
   408 	iTimer.Queue(KRcpIncomingButtonReleaseTimeout, *timerEntry);
       
   409 	}
       
   410 
       
   411 /** Callback when release timer expires.
       
   412 
       
   413 This is a static forwarding function.
       
   414 
       
   415 @param aExpiryInfo The information used by the real ReleaseExpiry to
       
   416 					deal with the timer expiry.
       
   417 */	
       
   418 TInt CPassthroughHelper::ReleaseExpiry(TAny* aExpiryInfo)
       
   419 	{
       
   420 	LOG_STATIC_FUNC
       
   421 	TAvrcpTimerExpiryInfo *timerInfo = reinterpret_cast<TAvrcpTimerExpiryInfo*>(aExpiryInfo);
       
   422 	(reinterpret_cast<CPassthroughHelper*>(timerInfo->iHandler))->ReleaseExpiry(timerInfo->iCommand);
       
   423 	
       
   424 	return KErrNone;
       
   425 	}
       
   426 
       
   427 /** Deals with expiry of release timer.
       
   428 
       
   429 1) Generate release for this command.
       
   430 2) Inform RemCon of available release command.
       
   431 
       
   432 @param aCommand	The CControlCommand that has expired.
       
   433 */
       
   434 void CPassthroughHelper::ReleaseExpiry(CControlCommand& aCommand)
       
   435 	{
       
   436 	LOG_FUNC
       
   437 	__ASSERT_DEBUG((aCommand.ButtonAct() == AVCPanel::EButtonPress), AvrcpUtils::Panic(EAvrcpReleaseExpiryForRelease));
       
   438 	__ASSERT_DEBUG(!aCommand.Click(), AvrcpUtils::Panic(EAvrcpReleaseExpiryForClick));
       
   439 	
       
   440 	// We haven't received a release within the allotted time.  Assume
       
   441 	// one and generate it to RemCon.
       
   442 	BalanceHandledCommand(aCommand);
       
   443 	}