--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetoothappprofiles/avrcp/remconbeareravrcp/src/passthroughhelper.cpp Mon Jan 18 20:28:57 2010 +0200
@@ -0,0 +1,443 @@
+// Copyright (c) 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 "avrcplog.h"
+#include "avrcprouter.h"
+#include "avrcputils.h"
+#include "controlcommand.h"
+#include "passthroughhelper.h"
+#include "controlbearer.h"
+
+
+CPassthroughHelper* CPassthroughHelper::NewL(CRcpRouter& aRouter, CRemConBearerAvrcp& aBearer, CDeltaTimer& aTimer)
+ {
+ CPassthroughHelper* helper = new(ELeave)CPassthroughHelper(aRouter, aBearer, aTimer);
+ CleanupStack::PushL(helper);
+ helper->ConstructL();
+ CleanupStack::Pop(helper);
+ return helper;
+ }
+
+CPassthroughHelper::~CPassthroughHelper()
+ {
+ }
+
+CPassthroughHelper::CPassthroughHelper(CRcpRouter& aRouter, CRemConBearerAvrcp& aBearer, CDeltaTimer& aTimer)
+ : iRouter(aRouter)
+ , iBearer(aBearer)
+ , iTimer(aTimer)
+ {
+ }
+
+void CPassthroughHelper::ConstructL()
+ {
+ }
+
+void CPassthroughHelper::Disconnect()
+ {
+ LOG_FUNC
+
+ // We handle the last command as a special case to ensure the presses and
+ // releases are balanced from a clients' point of view.
+ if(iPreviousPassthrough)
+ {
+ iRouter.RemoveFromSendQueue(*iPreviousPassthrough);
+ BalanceHandledCommand(*iPreviousPassthrough);
+ }
+ }
+
+/** Sends a response to the remote device.
+
+Because of the 100ms timeout for responses we send a response to
+passthrough commands as soon as we receive them. This means the real
+response from RemCon is currently ignored. A real response is
+only sent if this is a vendor dependent command.
+
+@param aCommand The command to respond to.
+@param aErr The result of handling the command. KErrNone if successful.
+ KErrNotSupported if this operation is not supported.
+*/
+void CPassthroughHelper::Respond(CControlCommand& aCommand, TInt aErr)
+ {
+ LOG_FUNC
+ aCommand.SetResponseType(aErr);
+ iRouter.AddToSendQueue(aCommand);
+ }
+
+/** To be called on completion of command handling.
+
+This aggregates the handler's tidying up of a finished
+command.
+
+@param aCommand The command to tidy up.
+*/
+void CPassthroughHelper::HandledCommand(CControlCommand& aCommand)
+ {
+ LOG_FUNC
+
+ // If this was the previous passthrough reset it to NULL
+ if(iPreviousPassthrough == &aCommand)
+ {
+ iPreviousPassthrough = NULL;
+ }
+
+ aCommand.CancelTimer(iTimer);
+ aCommand.DecrementUsers();
+ }
+
+/** Handles incoming passthrough command.
+
+How the command is handled is dependent on this and the previous command.
+
+Previous command This command Behaviour
+Press_op1 Press_op1 discard
+Press_op1 Release_op1 press_op1 hold timer not expired - generate click_op1
+ press_op1 release timer not expired - generate release_op1
+Press_op1 Press_op2 generate release_op1, start hold timer for press_op2
+Press_op1 Release_op2 press_op1 hold timer not expired - generate click_op1, discard release_op2
+ press_op1 release timer not expired - generate release_op1, discard release_op2
+Release_op1 Press_op1 start hold timer for press_op1
+Release_op1 Release_op1 discard
+Release_op1 Press_op2 start hold timer for press_op2
+Release_op1 Release_op2 discard
+
+@param aCommand The command to handle.
+@leave System wide error code.
+*/
+void CPassthroughHelper::HandlePassthrough(CControlCommand& aCommand)
+ {
+ LOG_FUNC
+
+ // We can't map Vendor Unique passthrough command to clicks as we do
+ // not know how the button action is conveyed to the API. All we can
+ // do is try and ensure that we try to generate a release if one is
+ // missing by asking the convert. We also need to consider them as
+ // part of the passthrough history for how other passthrough commands
+ // should be interpreted.
+ TUint8 thisOpId;
+ aCommand.Frame().OperationId(thisOpId);
+ if(!aCommand.IsAvrcpPassthrough())
+ {
+ aCommand.SetClick(EFalse);
+ }
+
+ // Behaviour depends on previous command
+ TUint8 prevOpId;
+ if(iPreviousPassthrough)
+ {
+ __ASSERT_ALWAYS(iPreviousPassthrough->ButtonAct() == AVCPanel::EButtonPress,
+ AvrcpUtils::Panic(EAvrcpPressNotPreviousPassthroughCommand));
+
+ iPreviousPassthrough->Frame().OperationId(prevOpId);
+ }
+
+ if(aCommand.ButtonAct() == AVCPanel::EButtonPress)
+ {
+ // Respond now so we can decrement users of aCommand at will
+ Respond(aCommand, KErrNone);
+
+ if(iPreviousPassthrough)
+ {
+ // previous is press, current is press
+ if(prevOpId == thisOpId)
+ {
+ // prevCommand is for same opId as this one. Drop this
+ // command and reset release timer for prevCommand.
+ aCommand.DecrementUsers();
+
+ iPreviousPassthrough->CancelTimer(iTimer);
+ // If still flagged as a click then the user won't have been notified
+ // of the press yet - so do it now.
+ if(iPreviousPassthrough->Click())
+ {
+ iPreviousPassthrough->SetClick(EFalse);
+ iPreviousPassthrough->SetCoreButtonAction(ERemConCoreApiButtonPress, ETrue);
+ iBearer.MrcciNewCommand(*iPreviousPassthrough, iPreviousPassthrough->ClientId());
+ }
+ StartReleaseTimer(*iPreviousPassthrough);
+ }
+ else if(iPreviousPassthrough->Click())
+ {
+ // prevCommand is click, Hold timer not expired. Generate click
+ // to RemCon then start waiting for click.
+ iPreviousPassthrough->CancelTimer(iTimer);
+ iPreviousPassthrough->SetCoreButtonAction(ERemConCoreApiButtonClick, ETrue);
+ iBearer.MrcciNewCommand(*iPreviousPassthrough, iPreviousPassthrough->ClientId());
+
+ HandledCommand(*iPreviousPassthrough);
+
+ // Start waiting for click (core api) or release (vendor unique)
+ NewPress(aCommand);
+ }
+ else
+ {
+ // prevCommand is not click, Release timer not expired. Generate
+ // release to RemCon then start waiting for click.
+ BalanceHandledCommand(*iPreviousPassthrough);
+
+ // Start waiting for click (core api) or release (vendor unique)
+ NewPress(aCommand);
+ }
+ }
+ else
+ {
+ // No previous command, current is press
+
+ // Start waiting for click (core api) or release (vendor unique)
+ NewPress(aCommand);
+ }
+ }
+ else if(iPreviousPassthrough)
+ {
+ // previous is press, current is release
+ if(prevOpId == thisOpId)
+ {
+ Respond(aCommand, KErrNone);
+
+ if(iPreviousPassthrough->Click())
+ {
+ // Button release for same opId with hold timeout
+ aCommand.DecrementUsers(); // Drop this command.
+
+ // Cancel hold timer and send the previous command as a click.
+ iPreviousPassthrough->CancelTimer(iTimer);
+ iPreviousPassthrough->SetCoreButtonAction(ERemConCoreApiButtonClick, ETrue);
+ iBearer.MrcciNewCommand(*iPreviousPassthrough, iPreviousPassthrough->ClientId());
+
+ HandledCommand(*iPreviousPassthrough);
+ }
+ else
+ {
+ // Button release for same opId, hold timeout expired
+ iPreviousPassthrough->CancelTimer(iTimer);
+ HandledCommand(*iPreviousPassthrough);
+
+ if(aCommand.IsAvrcpPassthrough())
+ {
+ aCommand.SetCoreButtonAction(ERemConCoreApiButtonRelease, ETrue);
+ }
+ iBearer.MrcciNewCommand(aCommand, aCommand.ClientId());
+ aCommand.DecrementUsers();
+ }
+ }
+ else
+ {
+ // Drop this release and continue waiting for release to
+ // prevCommand.
+ Respond(aCommand, KErrNone);
+ aCommand.DecrementUsers();
+ }
+ }
+ else
+ {
+ // No prevCommand, this is a release
+ // To get here either:
+ // - this is a spurious release
+ // - we've given up waiting for this
+
+ // Because responses must be made within 100ms we respond before waiting
+ // to see what RemCon thinks.
+ Respond(aCommand, KErrNone);
+ aCommand.DecrementUsers();
+ }
+ }
+
+void CPassthroughHelper::NewPress(CControlCommand& aCommand)
+ {
+ LOG_FUNC
+ __ASSERT_DEBUG(!iPreviousPassthrough, AVRCP_PANIC(EPreviousPassthroughNonNullReplacing));
+
+ iPreviousPassthrough = &aCommand;
+
+ if(aCommand.IsAvrcpPassthrough())
+ {
+ StartHoldTimer(aCommand);
+ }
+ else
+ {
+ iBearer.MrcciNewCommand(aCommand, aCommand.ClientId());
+ StartReleaseTimer(aCommand);
+ }
+ }
+
+/** To be called on completion of command handling for an unbalanced press.
+
+This is a special version of the HandledCommand which which assumes the
+command has been notified to the user and so balances the notification
+of a press with a generated release command.
+
+@param aCommand The command to tidy up.
+*/
+void CPassthroughHelper::BalanceHandledCommand(CControlCommand& aCommand)
+ {
+ LOG_FUNC
+ // CancelTimer also frees the timer entries, so needs to be called
+ // here even though there's no pending entry.
+ aCommand.CancelTimer(iTimer);
+
+ // If this was the previous passthrough reset it to NULL
+ if(iPreviousPassthrough == &aCommand)
+ {
+ iPreviousPassthrough = NULL;
+ }
+
+ // Here we handle the case that a press has been informed but a release has yet
+ // to be reported, and if it hasn't we provide the notification.
+ if(aCommand.ButtonAct() == AVCPanel::EButtonPress && !aCommand.Click())
+ {
+ if(aCommand.IsAvrcpPassthrough())
+ {
+ // FIXME I think we might need a new transaction id here?
+ TRAPD(err, aCommand.ReSetCoreButtonActionL(ERemConCoreApiButtonRelease, ETrue));
+ if (err == KErrNone)
+ {
+ iBearer.MrcciNewCommand(aCommand, aCommand.ClientId());
+ }
+ else
+ {
+ // just silently drop this command, do not send command to remconserv
+ }
+ }
+ else
+ {
+ // Need see if we can get a valid release from the converter. Pass
+ // it the same AVC Frame as the press, but with the button action
+ // flipped to release. If we can't do this then there's not a
+ // lot we can do.
+ // FIXME
+ }
+ }
+ aCommand.iHandlingLink.Deque();
+ aCommand.DecrementUsers();
+ }
+
+
+//------------------------------------------------------------------------------------
+// Timer functions
+//------------------------------------------------------------------------------------
+
+/** Starts the hold timer (only applicable to press commands).
+
+This is the timer to determine whether a command will be passed to RemCon
+as a click. If the timer expires before a matching release is received
+the press and release will be sent separately. Otherwise, a click is
+sent.
+
+@param aCommand The command to start the timer for.
+*/
+void CPassthroughHelper::StartHoldTimer(CControlCommand& aCommand)
+ {
+ LOG_FUNC
+
+ // These cannot fail as we use placement new
+ TAvrcpTimerExpiryInfo* timerInfo = new(aCommand.TimerExpiryInfo())TAvrcpTimerExpiryInfo(this, aCommand);
+
+ TCallBack callback(HoldExpiry, timerInfo);
+ TDeltaTimerEntry* timerEntry = new(aCommand.TimerEntry())TDeltaTimerEntry(callback);
+
+ iTimer.Queue(KRcpHoldThreshold, *timerEntry);
+ }
+
+/** Callback when hold timer expires.
+
+This is a static forwarding function.
+
+@param aExpiryInfo The information used by the real HoldExpiry to
+ deal with the timer expiry.
+*/
+TInt CPassthroughHelper::HoldExpiry(TAny* aExpiryInfo)
+ {
+ LOG_STATIC_FUNC
+ TAvrcpTimerExpiryInfo *timerInfo = reinterpret_cast<TAvrcpTimerExpiryInfo*>(aExpiryInfo);
+ (reinterpret_cast<CPassthroughHelper*>(timerInfo->iHandler))->HoldExpiry(timerInfo->iCommand);
+
+ return KErrNone;
+ }
+
+/** Deals with expiry of hold timer.
+
+1) This is not a click. Set click to false for this command.
+2) Inform RemCon of available press command
+3) Start release timer.
+
+@param aCommand The CControlCommand that has expired.
+*/
+void CPassthroughHelper::HoldExpiry(CControlCommand& aCommand)
+ {
+ LOG_FUNC
+ __ASSERT_DEBUG((aCommand.ButtonAct() == AVCPanel::EButtonPress), AvrcpUtils::Panic(EAvrcpHoldExpiryForRelease));
+
+ // We haven't received a release soon enough to treat this as
+ // a click. Send the press on to RemCon and wait for a release.
+ // CancelTimer also frees the timer entries, so needs to be called
+ // here even though there's no pending entry.
+ aCommand.CancelTimer(iTimer);
+ aCommand.SetClick(EFalse);
+ aCommand.SetCoreButtonAction(ERemConCoreApiButtonPress, ETrue);
+ iBearer.MrcciNewCommand(aCommand, aCommand.ClientId());
+ StartReleaseTimer(aCommand);
+ }
+
+/** Starts the release timer (only applicable to press commands).
+
+If a release is not received quickly enough to treat this press as
+a click, the release timer is started. 2s after receiving a button
+press we should assume the release if we haven't received one.
+
+@param aCommand The command to start the timer for.
+*/
+void CPassthroughHelper::StartReleaseTimer(CControlCommand& aCommand)
+ {
+ TAvrcpTimerExpiryInfo* timerInfo = new(aCommand.TimerExpiryInfo())TAvrcpTimerExpiryInfo(this, aCommand);
+
+ TCallBack callback(ReleaseExpiry, timerInfo);
+ TDeltaTimerEntry* timerEntry = new(aCommand.TimerEntry())TDeltaTimerEntry(callback);
+
+ iTimer.Queue(KRcpIncomingButtonReleaseTimeout, *timerEntry);
+ }
+
+/** Callback when release timer expires.
+
+This is a static forwarding function.
+
+@param aExpiryInfo The information used by the real ReleaseExpiry to
+ deal with the timer expiry.
+*/
+TInt CPassthroughHelper::ReleaseExpiry(TAny* aExpiryInfo)
+ {
+ LOG_STATIC_FUNC
+ TAvrcpTimerExpiryInfo *timerInfo = reinterpret_cast<TAvrcpTimerExpiryInfo*>(aExpiryInfo);
+ (reinterpret_cast<CPassthroughHelper*>(timerInfo->iHandler))->ReleaseExpiry(timerInfo->iCommand);
+
+ return KErrNone;
+ }
+
+/** Deals with expiry of release timer.
+
+1) Generate release for this command.
+2) Inform RemCon of available release command.
+
+@param aCommand The CControlCommand that has expired.
+*/
+void CPassthroughHelper::ReleaseExpiry(CControlCommand& aCommand)
+ {
+ LOG_FUNC
+ __ASSERT_DEBUG((aCommand.ButtonAct() == AVCPanel::EButtonPress), AvrcpUtils::Panic(EAvrcpReleaseExpiryForRelease));
+ __ASSERT_DEBUG(!aCommand.Click(), AvrcpUtils::Panic(EAvrcpReleaseExpiryForClick));
+
+ // We haven't received a release within the allotted time. Assume
+ // one and generate it to RemCon.
+ BalanceHandledCommand(aCommand);
+ }