diff -r 16e4b9007960 -r f5508c13dfe0 bluetoothappprofiles/avrcp/remconbeareravrcp/src/avrcpoutgoingcommandhandler.cpp --- a/bluetoothappprofiles/avrcp/remconbeareravrcp/src/avrcpoutgoingcommandhandler.cpp Wed Oct 13 13:15:31 2010 +0300 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,768 +0,0 @@ -// 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: -// - - - -/** - @file - @internalComponent - @released -*/ - -#include - -#include "avcpanel.h" -#include "controlcommand.h" -#include "avrcpoutgoingcommandhandler.h" -#include "avrcplog.h" -#include "avrcprouter.h" -#include "avrcptimer.h" -#include "avrcputils.h" -#include "controlbearer.h" - -//--------------------------------------------------------------------- -// Construction/Destruction -//--------------------------------------------------------------------- - -/** Factory function. - -@param aBearer The CRemConBearerAvrcp this is to handle commands for. -@param aObserver The observer of the bearer. Used to aquire converters. -@param aRouter A CRcpRouter to use for communication with remote devices. -@param aTimer CDeltaTimer to use for queuing timed events. -@return A fully constructed CRcpOutgoingCommandHandler. -@leave System wide error codes. -*/ -CRcpOutgoingCommandHandler* CRcpOutgoingCommandHandler::NewL(MRemConControlCommandInterface& aCommandInterface, - MRemConBearerObserver& aObserver, - CRcpRouter& aRouter, - CDeltaTimer& aTimer) - { - LOG_STATIC_FUNC - CRcpOutgoingCommandHandler* handler = new(ELeave)CRcpOutgoingCommandHandler(aCommandInterface, aObserver, aRouter, aTimer); - return handler; - } - -/** Constructor. - -@param aBearer The CRemConBearerAvrcp this is to handle commands for. -@param aObserver The observer of the bearer. Used to aquire converters. -@param aRouter A CRcpRouter to use for communication with remote devices. -@param aTimer CDeltaTimer to use for queuing timed events. -@return A partially constructed CRcpIncomingCommandHandler. -@leave System wide error codes. -*/ -CRcpOutgoingCommandHandler::CRcpOutgoingCommandHandler(MRemConControlCommandInterface& aCommandInterface, - MRemConBearerObserver& aObserver, - CRcpRouter& aRouter, - CDeltaTimer& aTimer) : iCommandQueue(_FOFF(CControlCommand, iHandlingLink)), - iNotifyCommandQueue(_FOFF(CControlCommand, iHandlingLink)), - iCommandInterface(aCommandInterface), iObserver(aObserver), iRouter(aRouter), iTimer(aTimer) - { - LOG_FUNC - } - -/** Destructor. -*/ -CRcpOutgoingCommandHandler::~CRcpOutgoingCommandHandler() - { - LOG_FUNC - - ClearQueue(iCommandQueue); - ClearQueue(iNotifyCommandQueue); - } - -void CRcpOutgoingCommandHandler::ClearQueue(TDblQue& aQue) - { - while(!aQue.IsEmpty()) - { - CControlCommand* command = aQue.First(); - command->CancelTimer(iTimer); - command->iHandlingLink.Deque(); - command->DecrementUsers(); - } - } -//--------------------------------------------------------------------- -// Called from the bearer -//--------------------------------------------------------------------- - -/** Tell the handler to gracefully shutdown. - -@param aClearQueue Whether to clear the queue without handling the things - on it. If this is true the commands will be deleted. - If this is false then pending commands will have responses - generated to RemCon. -*/ -void CRcpOutgoingCommandHandler::Disconnect(TBool aClearQueue) - { - LOG_FUNC - ProcessDisconnect(iCommandQueue, aClearQueue); - ProcessDisconnect(iNotifyCommandQueue, aClearQueue); - } - -void CRcpOutgoingCommandHandler::ProcessDisconnect(TDblQue& aQue, TBool aClearQueue) - { - while(!aQue.IsEmpty()) - { - CControlCommand* command = aQue.First(); - iRouter.RemoveFromSendQueue(*command); - command->CancelTimer(iTimer); - - if(aClearQueue) - { - GenerateFailureResult(*command, KErrDisconnected); - } - - command->iHandlingLink.Deque(); - command->DecrementUsers(); - } - } -/** Sends a new command. - -@param aInterfaceUid The RemCon client interface this command is from. -@param aCommand The operation id within aInterfaceUid. -@param aId A unique identifier provided by RemCon. -@param aCommandData Data associated with this command. -@param aAddr Bluetooth address of device to send this command to. -@leave KErrNoMemory or system wide error code. -@leave Command parsing error. -*/ -void CRcpOutgoingCommandHandler::SendCommandL(TUid aInterfaceUid, - TUint aCommand, - TUint aId, - RBuf8& aCommandData, - const TBTDevAddr& aAddr) - { - LOG_FUNC - - if(aInterfaceUid.iUid == KRemConCoreApiUid) - { - // Passthrough commands are stateful, so we need to examine the - // history - we can't just blindly wham it on the queue. - HandleCoreApiCommandL(aCommand, aId, aCommandData, aAddr); - } - else - { - SendCommandL(aInterfaceUid, aCommand, aId, aCommandData, EFalse, aAddr, ETrue, EFalse); - } - } - -/** Sends a new notify command. - -@param aInterfaceUid The RemCon client interface this command is from. -@param aCommand The operation id within aInterfaceUid. -@param aId A unique identifier provided by RemCon, the transaction ID. -@param aCommandData Data associated with this command. -@param aAddr Bluetooth address of device to send this command to. -@leave KErrNoMemory or system wide error code. -@leave Command parsing error. -*/ -void CRcpOutgoingCommandHandler::SendNotifyCommandL(TUid aInterfaceUid, - TUint aCommand, - TUint aId, - RBuf8& aCommandData, - const TBTDevAddr& aAddr) - { - LOG_FUNC - SendCommandL(aInterfaceUid, aCommand, aId, aCommandData, EFalse, aAddr, ETrue, ETrue); - } - -//--------------------------------------------------------------------- -// Data notifications from the router -//--------------------------------------------------------------------- - -/** Called by the router to provide a new response. - -@param aFrame The AV/C frame for this response. Ownership is taken. -@param aTransLabel The AVCTP transaction id of this response. This is used - to match it with its command. -@param aAddr The remote from which this response originated -*/ -void CRcpOutgoingCommandHandler::ReceiveResponse(const TDesC8& aMessageInformation, - SymbianAvctp::TTransactionLabel aTransLabel, - TBool aIpidBitSet) - { - LOG_FUNC - - CAVCFrame* frame = NULL; - TInt err = KErrNone; - if(!aIpidBitSet) - { - TRAP(err, frame = CAVCFrame::NewL(aMessageInformation, AVC::EResponse)); - } - - if(!err) - { - CControlCommand* command = NULL; - command = FindInQueue(iCommandQueue, aTransLabel); - if ( command != NULL ) - { - //Found, so it is a normal command response. - ProcessReceiveResponse(frame, aIpidBitSet, command, EFalse); - } - else - { - //Try to find in the notify command queue. - command = FindInQueue(iNotifyCommandQueue, aTransLabel); - if( command != NULL ) - { - //Found, so it is a notify command response. - ProcessReceiveResponse(frame, aIpidBitSet, command, ETrue); - } - } - - delete frame; - } - } - -CControlCommand* CRcpOutgoingCommandHandler::FindInQueue(TDblQue& aQue, - SymbianAvctp::TTransactionLabel aTransLabel) - { - CControlCommand* command = NULL; - TDblQueIter iter(aQue); - while (iter) - { - command = iter++; - if(command->TransactionLabel() == aTransLabel) - { - return command; - } - } - - return NULL; - } - -void CRcpOutgoingCommandHandler::ProcessReceiveResponse(CAVCFrame* aFrame, - TBool aIpidBitSet, - CControlCommand* aCommand, - TBool aNotify) - { - aCommand->CancelTimer(iTimer); - - TInt err = KErrNone; - // Inform the bearer if this is something it knows about - // ie not a click release - if(aCommand->KnownToBearer()) - { - if(!aIpidBitSet) - { - if(aFrame->Data().Length() < KAVCFrameHeaderLength) - { - // Drop corrupt frames - return; - } - - err = aCommand->ParseIncomingResponse(iObserver, *aFrame); - } - else - { - // If aIpidBitSet is true that means AVRCP is not supported - // by the remote end. We handle this in the same way as not - // supported commands, passing them up to RemCon as not - // supported, so just map the ctype here, rather than setting - // up another path for ipid handling, but we need pass as the - // frame the original because we don't get one from AVCTP if - // ipid is set. - aCommand->SetResponseType(KErrNotSupported); - err = aCommand->ParseIncomingResponse(iObserver, aCommand->Frame()); - } - - if ( aNotify ) - {//This is a notify command - iCommandInterface.MrccciNewNotifyResponse(*aCommand); - } - else - { - iCommandInterface.MrccciNewResponse(*aCommand); - } - } - - TBool doDeque = ETrue; - if ( (!aIpidBitSet) && (err == KErrNone) && (aNotify) && (aFrame->Type() == AVC::EInterim)) - { - doDeque = EFalse; - } - - // If this a passthrough press that hasn't yet been released, we need - // to wait for a release before getting rid of this, otherwise we're done. - if(aCommand == iUnreleasedCommand) - { - iUnreleasedHasResponse = ETrue; - StartReleaseTimer(*iUnreleasedCommand); - doDeque = EFalse; - } - - if ( doDeque ) - { - aCommand->iHandlingLink.Deque(); - aCommand->DecrementUsers(); - } - } -/** Called by the router to complete a send. - -@param aCommand The command which has been sent. -@param aSendResult The result of the send. KErrNone if successful. -*/ -void CRcpOutgoingCommandHandler::MessageSent(CAvrcpCommand& aCommand, TInt aSendResult) - { - LOG_FUNC - - if(aSendResult == KErrNone) - { - // Set off response timer - StartResponseTimer(static_cast(aCommand)); - } - else - { - CControlCommand* command = FindInQueue(iNotifyCommandQueue, aCommand.TransactionLabel()); - - if(command) - { - command->SetNotifyVolumeChangeResult(command->Frame()); - iCommandInterface.MrccciNewNotifyResponse(*command); - } - else - { - command = FindInQueue(iCommandQueue, aCommand.TransactionLabel()); - - // Generate error response up to RemCon - // if this is a core command we can set the result, - // otherwise we just return it as we got it. - if(command->Frame().Opcode() == AVC::EPassThrough) - { - // Need to insert before setting the button action so we have - // long enough data - if (!command->InsertCoreResult(aSendResult)) - { - if(command->Click()) - { - command->SetCoreButtonAction(ERemConCoreApiButtonClick, ETrue); - } - } - } - - iCommandInterface.MrccciNewResponse(*command); - } - - command->iHandlingLink.Deque(); - command->DecrementUsers(); - } - } - -//--------------------------------------------------------------------- -// Internal Utility functions -//--------------------------------------------------------------------- - -void CRcpOutgoingCommandHandler::CleanupUnreleased() - { - iUnreleasedCommand->CancelTimer(iTimer); - iUnreleasedCommand->iHandlingLink.Deque(); - iUnreleasedCommand->DecrementUsers(); - iUnreleasedHasResponse = EFalse; - } - -/** Handle a command that is part of the Core API. - -@param aCommand The operation id within aInterfaceUid. -@param aId A unique identifier provided by RemCon. -@param aCommandData Data associated with this command. -@param aAddr Bluetooth address of device to send this command to. -@leave KErrNoMemory or system wide error code. -@leave Command parsing error. -*/ -void CRcpOutgoingCommandHandler::HandleCoreApiCommandL(TUint aCommand, - TUint aId, - RBuf8& aCommandData, - const TBTDevAddr& aAddr) - { - if(aCommandData.Length() < KRemConCoreApiButtonDataLength) - { - User::Leave(KErrCorrupt); - } - - TInt buttonData; - AvrcpUtils::ReadCommandDataToInt(aCommandData, - KRemConCoreApiButtonDataOffset, KRemConCoreApiButtonDataLength, buttonData); - - // First check if there's anything we need to do before sending this command, - // mainly releasing a previous press. - if(iUnreleasedCommand) - { - TUint prevOpId = iUnreleasedCommand->RemConOperationId(); - - if(aCommand == prevOpId) - { - // Either we've received a release, or we've refreshed the press. - // If the unreleased press has already been responded too we can - // dispose of it now. - // If the unreleased press has not been responded too we can - // treat it like a normal command on reception of response, so just - // set iUnreleased to NULL. - if(iUnreleasedHasResponse) - { - CleanupUnreleased(); - } - - iUnreleasedCommand = NULL; - } - else - { - // A new operation! - if(buttonData != ERemConCoreApiButtonRelease) - { - // Try and generate the release for the previous command, if - // if fails then the remote will just have to assume it. - // There's no point leaving this to the release timer, because - // we want to send another command now, so even if we send the - // release later the remote should ignore it. - TRAP_IGNORE(GenerateCommandL(*iUnreleasedCommand, ERemConCoreApiButtonRelease)); - - if(iUnreleasedHasResponse) - { - CleanupUnreleased(); - } - - iUnreleasedCommand = NULL; - } - else - { - // A release for a command other than iUnreleased. We can't - // send this now. - User::Leave(KErrNotReady); - } - } - } - else if(buttonData == ERemConCoreApiButtonRelease) - { - // We don't have an unreleased command. We must have already - // released this, either via the timer, or because we've sent - // another command in the meantime. We can't send this now. - // Leaving synchronously means we don't need to worry about generating - // a fake response, which may mislead the application. - User::Leave(KErrNotReady); - } - - if(buttonData == ERemConCoreApiButtonClick) - { - // aCommandData is still owned by RemCon until we return successfully. - // If we try the operations with the new data first we won't end up - // in a situation where the new CControlCommand thinks that it owns - // aCommandData, then the release operation leaves, so RemCon also - // thinks it owns aCommandData. - - RBuf8 pressBuf; - pressBuf.CreateL(aCommandData); - CleanupClosePushL(pressBuf); - - AvrcpUtils::SetCommandDataFromInt(pressBuf, - KRemConCoreApiButtonDataOffset, KRemConCoreApiButtonDataLength, ERemConCoreApiButtonPress); - SendCommandL(TUid::Uid(KRemConCoreApiUid), aCommand, aId, pressBuf, ETrue, aAddr, ETrue, EFalse); - - // Data has been taken ownership of by SendCommandL, so can just let - // pressbuf go out of scope. - CleanupStack::Pop(&pressBuf); - - AvrcpUtils::SetCommandDataFromInt(aCommandData, - KRemConCoreApiButtonDataOffset, KRemConCoreApiButtonDataLength, ERemConCoreApiButtonRelease); - SendCommandL(TUid::Uid(KRemConCoreApiUid), aCommand, aId, aCommandData, ETrue, aAddr, EFalse, EFalse); - } - else if(buttonData == ERemConCoreApiButtonPress) - { - iUnreleasedCommand = &SendCommandL(TUid::Uid(KRemConCoreApiUid), aCommand, aId, aCommandData, EFalse, aAddr, ETrue, EFalse); - iReleaseTimerExpiryCount = 0; - } - else - { - // Must be release - __ASSERT_DEBUG(buttonData == ERemConCoreApiButtonRelease, AvrcpUtils::Panic(EAvrcpUnknownButtonAction)); - SendCommandL(TUid::Uid(KRemConCoreApiUid), aCommand, aId, aCommandData, EFalse, aAddr, ETrue, EFalse); - } - } - -/** Creates a command from the RemCon data. - -This is an internal utility function. - -A CControlCommand will be created. Calling ProcessOutgoingCommandL on -this creates a CAVCFrame from the provided data. If an AV/C frame -can be created the command will be added to the outgoing queue to -wait a response from the remote. Otherwise this function will -leave. - -@param aInterfaceUid The RemCon client interface this command is from. -@param aCommand The operation id within aInterfaceUid. -@param aId A unique identifier provided by RemCon. -@param aCommandData Data associated with this command. -@param aIsClick Whether this is a button click. -@param aAddr Bluetooth address of device to send this command to. -@return The generated command. -@leave KErrNoMemory or system wide error code. -@leave Command parsing error. -*/ -CControlCommand& CRcpOutgoingCommandHandler::SendCommandL(TUid aInterfaceUid, - TUint aCommand, - TUint aId, - RBuf8& aCommandData, - TBool aIsClick, - const TBTDevAddr& aAddr, - TBool aKnownToBearer, - TBool aNotify) - { - LOG_FUNC - // Create a command and wham it on our queue, so we can match it up with its response - // CControlCommand::NewL takes ownership of the data in aCommandData then NULLs aCommandData - // so a leave later in the function won't cause double deletion. - CControlCommand* command = CControlCommand::NewL(aInterfaceUid, aCommand, aId, iCurrentTrans, - aCommandData, aIsClick, aAddr, aKnownToBearer); - CleanupStack::PushL(command); - - command->ProcessOutgoingCommandL(iObserver); - CleanupStack::Pop(command); - command->IncrementUsers(); - - if ( aNotify ) - { - iNotifyCommandQueue.AddLast(*command); - } - else - { - iCommandQueue.AddLast(*command); - } - - // Increment our transaction id - iCurrentTrans = (iCurrentTrans + 1) % SymbianAvctp::KMaxTransactionLabel; - - // Command stays on the queue till we've got the response - iRouter.AddToSendQueue(*command); - - return *command; - } - -/** Generate a failure response to RemCon. - -This sets the result for a passthrough command. -It informs the bearer of the new response. - -@param aCommand The command to finish off. -@param aResult The result (only valid for passthrough) -*/ -void CRcpOutgoingCommandHandler::GenerateFailureResult(CControlCommand& aCommand, TInt aResult) - { - // Response is only necessary if the bearer knows about this command. - if(aCommand.KnownToBearer() && (aCommand.Frame().Opcode() == AVC::EPassThrough)) - { - if (aCommand.InsertCoreResult(aResult) == KErrNone) - { - if(aCommand.Click()) - { - aCommand.SetCoreButtonAction(ERemConCoreApiButtonClick, ETrue); - } - else if(aCommand.ButtonAct() == AVCPanel::EButtonPress) - { - aCommand.SetCoreButtonAction(ERemConCoreApiButtonPress, ETrue); - } - else - { - aCommand.SetCoreButtonAction(ERemConCoreApiButtonRelease, ETrue); - } - - iCommandInterface.MrccciNewResponse(aCommand); - } - } - } - -/** Generate a command to the remote. - -This is needed in situations where the application has not met the avrcp -button refresh requirements so we need to internally generate something -to stop the remote getting a bad impression of us. - -@param aCommand The command to be issue again. -*/ -void CRcpOutgoingCommandHandler::GenerateCommandL(CControlCommand& aCommand, TRemConCoreApiButtonAction aButtonAct) - { - LOG_FUNC - - RBuf8 commandData; - commandData.CreateMaxL(KRemConCoreApiButtonDataLength); - - AvrcpUtils::SetCommandDataFromInt(commandData, - KRemConCoreApiButtonDataOffset, KRemConCoreApiButtonDataLength, aButtonAct); - - // This will not leave before taking ownership of commandData. - SendCommandL(aCommand.RemConInterfaceUid(), aCommand.RemConOperationId(), aCommand.RemConCommandId(), commandData, EFalse, - aCommand.RemoteAddress(), EFalse, EFalse); - } - -//------------------------------------------------------------------------------------ -// Timer functions -//------------------------------------------------------------------------------------ - -/** Starts the response timer. - -AVRCP mandates a remote respond within 100ms of receiving a command. -This is the timer for that, and is started when a command has -successfully been sent. - -@param aCommand The command to start the timer for. -*/ -void CRcpOutgoingCommandHandler::StartResponseTimer(CControlCommand& aCommand) - { - LOG_FUNC - // These use placement new, so cannot fail - TAvrcpTimerExpiryInfo* timerInfo = new(aCommand.TimerExpiryInfo())TAvrcpTimerExpiryInfo(this, aCommand); - - TCallBack callback(ResponseExpiry, timerInfo); - TDeltaTimerEntry* timerEntry = new(aCommand.TimerEntry())TDeltaTimerEntry(callback); - - iTimer.Queue(KRcpResponseTimeOut, *timerEntry); - } - -/** Callback when response timer expires. - -This is a static forwarding function. - -@param aExpiryInfo The information used by the real ResponseExpiry to - deal with the timer expiry. -*/ -TInt CRcpOutgoingCommandHandler::ResponseExpiry(TAny* aExpiryInfo) - { - LOG_STATIC_FUNC - TAvrcpTimerExpiryInfo *timerInfo = reinterpret_cast(aExpiryInfo); - (reinterpret_cast(timerInfo->iHandler))->ResponseExpiry(timerInfo->iCommand); - - return KErrNone; - } - -/** Deals with response timeout. - -This sends a timeout response to RemCon. - -@param aCommand The CControlCommand that has expired. -*/ -void CRcpOutgoingCommandHandler::ResponseExpiry(CControlCommand& aCommand) - { - LOG_FUNC - - GenerateFailureResult(aCommand, KErrTimedOut); - - // Failed to get a response to this, don't bother about trying - // to release it. - if(iUnreleasedCommand == &aCommand) - { - iUnreleasedCommand = NULL; - __ASSERT_DEBUG(!iUnreleasedHasResponse, AvrcpUtils::Panic(EAvrcpPressHasPhantomResponse)); - } - - aCommand.iHandlingLink.Deque(); - aCommand.DecrementUsers(); - } - -/** Starts the release timer. - -AVRCP requires a press to be refreshed less than 2s after the first -press, if it is not to be assumed to have been released. We pass -this requirement on to RemCon clients as we don't know when they might -go away and we don't want to keep buttons pressed forever. If the -release timer expires we will assume a release, and generate it to -the remote. - -@param aCommand The command to start the timer for. -*/ -void CRcpOutgoingCommandHandler::StartReleaseTimer(CControlCommand& aCommand) - { - LOG_FUNC - - // These cannot fail as we use placement new - TAvrcpTimerExpiryInfo* timerInfo = new(aCommand.TimerExpiryInfo())TAvrcpTimerExpiryInfo(this, aCommand); - - TCallBack callback(ReleaseExpiry, timerInfo); - TDeltaTimerEntry* timerEntry = new(aCommand.TimerEntry())TDeltaTimerEntry(callback); - - iTimer.Queue(KRcpOutgoingButtonReleaseTimeout, *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 CRcpOutgoingCommandHandler::ReleaseExpiry(TAny* aExpiryInfo) - { - LOG_STATIC_FUNC - TAvrcpTimerExpiryInfo *timerInfo = reinterpret_cast(aExpiryInfo); - (reinterpret_cast(timerInfo->iHandler))->ReleaseExpiry(timerInfo->iCommand); - - return KErrNone; - } - -/** Deals with expiry of release timer. - -1) Generate release for this command. -2) Send release to remote. - -@param aCommand The CControlCommand that has expired. -*/ -void CRcpOutgoingCommandHandler::ReleaseExpiry(CControlCommand& aCommand) - { - LOG_FUNC - __ASSERT_DEBUG((aCommand.ButtonAct() == AVCPanel::EButtonPress), AvrcpUtils::Panic(EAvrcpReleaseExpiryForRelease)); - __ASSERT_DEBUG(&aCommand == iUnreleasedCommand, AvrcpUtils::Panic(EAvrcpReleaseExpiryForOldCommand)); - __ASSERT_DEBUG(iUnreleasedHasResponse, AvrcpUtils::Panic(EAvrcpReleaseTimerStartedWithoutResponse)); - - iReleaseTimerExpiryCount++; - - TBool commandCompleted = ETrue; - // If the client is not yet obliged to refresh this, then send another press. Otherwise generate - // the release for them. - if((iReleaseTimerExpiryCount * KRcpOutgoingButtonReleaseTimeout) < KRemConCoreApiPressRefreshInterval) - { - // This will try and generate a press that is identical to the original - // aCommand, but with a new AVCTP transaction id. - TRAPD(err, GenerateCommandL(aCommand, ERemConCoreApiButtonPress)); - - if(!err) - { - // Start the timer again on the original command - StartReleaseTimer(aCommand); - commandCompleted = EFalse; - } - } - else - { - // Try an generate a release, but if it fails we just have to let the - // remote assume it. - TRAP_IGNORE(GenerateCommandL(aCommand, ERemConCoreApiButtonRelease)); - } - - // This condition may be true because - // - we have failed to generate a press, in which case the remote is entitled - // to assume this is released, so we just give up on it. - // or - // - the client has not met its press refresh obligation (whether we've - // successfully generated a release or not. - // In either case we won't do anything more with this command. - if(commandCompleted) - { - aCommand.iHandlingLink.Deque(); - aCommand.DecrementUsers(); - - iUnreleasedCommand = NULL; - iUnreleasedHasResponse = EFalse; - iReleaseTimerExpiryCount = 0; - } - }