bluetoothmgmt/bluetoothclientlib/avctpservices/avctpremotedevices.cpp
changeset 0 29b1cd4cb562
child 51 20ac952a623c
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetoothmgmt/bluetoothclientlib/avctpservices/avctpremotedevices.cpp	Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,800 @@
+// Copyright (c) 2005-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
+ @internalTechnology
+*/
+
+#include <bluetooth/logger.h>
+#include <bt_sock.h>
+
+#include "avctpremotedevices.h"
+#include "avctpbody.h"
+#include "avctpserviceutils.h"
+#include "avctpcommon.h"
+#include "avctpPriorities.h"
+#include "avctpserviceutils.h"
+#include "channelcontrollers.h"
+
+using namespace SymbianAvctp;
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_AVCTP_SERVICES);
+#endif
+
+RNestableLock::RNestableLock()
+	: iRefCount(0)
+	, iThreadId(KInvalidThreadId)	// set to an invalid id
+	{
+	}
+
+TInt RNestableLock::CreateLocal()
+	{
+	TInt err = iLock.CreateLocal();
+	if(err == KErrNone)
+		{
+		err = iMetaLock.CreateLocal();
+		}
+	if(err != KErrNone)
+		{
+		Close();
+		}
+	return err;
+	}
+
+void RNestableLock::Close()
+	{
+	iLock.Close();
+	iMetaLock.Close();
+	iRefCount = 0;
+	}
+
+void RNestableLock::Wait()
+	{
+	iMetaLock.Wait();
+	TThreadId currentThreadId = RThread().Id();
+	if(iThreadId == TThreadId(KInvalidThreadId) || currentThreadId != iThreadId)
+		{
+		iMetaLock.Signal();
+		iLock.Wait();
+		iMetaLock.Wait();
+		iThreadId = currentThreadId;
+		}
+	++iRefCount;
+	iMetaLock.Signal();
+	}
+
+void RNestableLock::Signal()
+	{
+	iMetaLock.Wait();
+	// Assert if current thread is stored current thread?
+	if(--iRefCount == 0)
+		{
+		iLock.Signal();
+		iThreadId = TThreadId(KInvalidThreadId);
+		}
+	iMetaLock.Signal();
+	}
+
+TAvctpRemoteDeviceInfo::TAvctpRemoteDeviceInfo(const TBTDevAddr& aAddr) :
+	iAddr(aAddr),
+	iHasSecondChannel(EFalse)
+	{
+	LOG_FUNC
+	}
+
+const TBTDevAddr& TAvctpRemoteDeviceInfo::RemoteAddress() const
+	{
+	LOG_FUNC
+	return iAddr;
+	}
+
+TBool TAvctpRemoteDeviceInfo::HasSecondChannel() const
+	{
+	LOG_FUNC
+	return iHasSecondChannel;
+	}
+
+void TAvctpRemoteDeviceInfo::SetHasSecondChannel(TBool aHasSecondChannel)
+	{
+	LOG_FUNC
+	iHasSecondChannel = aHasSecondChannel;
+	}
+
+/**
+Two phase constructor of CAvctpRemoteDevices which deals with connection and disconnection 
+of a remote device. 
+@param aNotify provides access to the MAvctpEventNotify
+@param aSocketServ a connected comms session
+@internalComponent
+*/
+CAvctpRemoteDevices* CAvctpRemoteDevices::NewL(MAvctpEventNotify& aNotify, RSocketServ& aSocketServ, SymbianAvctp::TPid aPid)
+	{
+	LOG_STATIC_FUNC
+
+	CAvctpRemoteDevices* self = CAvctpRemoteDevices::NewLC(aNotify, aSocketServ, aPid);
+	CleanupStack::Pop(self);
+	return self;
+	}
+	
+/**
+Two phase constructor of CAvctpRemoteDevices which deals with connection and disconnection 
+of a remote device. 
+This leaves newly created object on cleanupstack
+@param aAvctpBody provides access to the MAvctpEventNotify
+@param aSocketServ a connected comms session
+@internalComponent
+*/
+CAvctpRemoteDevices* CAvctpRemoteDevices::NewLC(MAvctpEventNotify& aNotify, RSocketServ& aSocketServ, SymbianAvctp::TPid aPid)
+	{
+	LOG_STATIC_FUNC
+
+	CAvctpRemoteDevices* self = new(ELeave) CAvctpRemoteDevices(aNotify, aSocketServ);
+	CleanupStack::PushL(self);
+	self->ConstructL(aPid);
+	return self;	
+	}
+
+/**
+c'tor
+@internalComponent
+*/
+CAvctpRemoteDevices::CAvctpRemoteDevices(MAvctpEventNotify& aNotify, RSocketServ& aSocketServ):
+ 	iNotify(aNotify),
+	iSocketServ(aSocketServ)
+	{
+	LOG_FUNC
+	}
+	
+
+/** 
+@internalComponent
+*/
+void CAvctpRemoteDevices::ConstructL(SymbianAvctp::TPid aPid)
+	{
+	LOG_FUNC
+
+	User::LeaveIfError(iLock.CreateLocal()); 
+	
+	iPid = aPid;
+	iState = EListening;
+	
+	iPrimaryChannelController = CPrimaryChannelController::NewL(*this, iSocketServ, aPid);	
+	iPrimaryChannelController->Listen();
+	}
+
+	
+/**
+d'tor
+@internalComponent
+*/	
+CAvctpRemoteDevices::~CAvctpRemoteDevices()
+	{
+	LOG_FUNC
+	iLock.Wait();
+	iLock.Close();
+
+	iRemoteAddrs.Close();
+	delete iSecondaryChannelController;
+	delete iPrimaryChannelController;
+	}
+
+/**
+Starts a connection to a remote device
+@param aBTDevice the remote device to connect to
+@internalComponent
+*/
+TInt CAvctpRemoteDevices::PrimaryChannelAttachRequest(const TBTDevAddr& aBTDevice)
+	{
+	LOG_FUNC
+	
+	iLock.Wait();
+
+	TInt err = KErrRepeatConnectionAttempt;
+
+	// Start the connection attempt
+	
+	if (iState == EListening)
+		{
+		if (!IsAttached(aBTDevice))
+			{
+			iState = ECreatingControlLink;
+			iPrimaryChannelController->AttachRequest(aBTDevice);
+			err = KErrNone;
+			}
+		}
+	
+	if (err != KErrNone)
+		{
+		iState = EListening;
+		}
+	
+	iLock.Signal();
+	return err;
+	}
+
+/**
+Called after a Attach Indicate. If the connection hasn't been accepted it send back to the server
+a refuse attach ioctl, and the client won't be notified through the controlling channel
+@param aBTDevice the remote device to add
+@param aClientsAccepts ETrue if the client has accepted the Attach Indication or
+					   EFalse if the client has denied the Attach Indication
+@param aChannel the controlling channel through the indication has been received
+@internalComponent
+*/
+TInt CAvctpRemoteDevices::PropagateAttachRsp(const TBTDevAddr& aBTDevice, 
+							  			      TBool aClientsAccepts,
+							  			      TInt aChannel)
+	{
+	LOG_FUNC
+
+	TInt err = KErrNone;
+
+	if (aClientsAccepts) 
+		{
+		if (err == KErrNone && !IsAttached(aBTDevice) && aChannel == KAvctpPrimaryChannel)
+			{
+			//only add device on primary control channel connection
+			TAvctpRemoteDeviceInfo info(aBTDevice);
+			err = iRemoteAddrs.Append(info);
+			}
+		}
+	else
+		{
+		__ASSERT_DEBUG(aChannel == KAvctpPrimaryChannel || aChannel == KAvctpSecondaryChannel, Panic(EAvctpInvalidChannel));
+		
+		CBaseChController* pointer = (aChannel == KAvctpPrimaryChannel) ? ((CBaseChController*)iPrimaryChannelController) : ((CBaseChController*)iSecondaryChannelController);
+		
+		__ASSERT_DEBUG(pointer, Panic(EAvctpNullControllerChannel));
+		pointer->RefuseAttach(aBTDevice);	// won't receive a confirmation
+		}	
+	
+	return err;
+	}
+	
+/**
+Explicitly disconnect from the give remote device.
+@internalComponent
+*/
+TInt CAvctpRemoteDevices::PrimaryChannelDetachRequest(const TBTDevAddr& aBTDevice)
+	{
+	LOG_FUNC
+	
+	iLock.Wait();
+	
+	TInt err = KErrFirstChannelNotAttached;
+	const TAvctpRemoteDeviceInfo* info = RemoteDeviceInfo(aBTDevice);
+	if (info)
+		{
+		iPrimaryChannelController->DetachRequest(aBTDevice);
+		err = KErrNone;
+		}
+	
+	iLock.Signal();
+	
+	// else covered by ret
+	return err;
+	}
+
+void CAvctpRemoteDevices::PrimaryChannelCancelAttach(const TBTDevAddr& aBTDevice)
+	{
+	LOG_FUNC
+	
+	iLock.Wait();
+	
+	if (!IsAttached(aBTDevice) && iState == ECreatingControlLink)
+		{
+		// the notifications are only propagated if the state is indicating an
+		// outstanding request. So, simply changing the state to listening will avoid
+		// the notification to be propagated. 
+		// We also need to notify the stack that we are not interested so to have a consistent state
+		// It is safe to call RefuseAttach that deal with this scenario server side.
+		iPrimaryChannelController->RefuseAttach(aBTDevice);
+		iState = EListening;	
+		}
+	
+	iLock.Signal();
+	}
+
+TInt CAvctpRemoteDevices::SecondaryChannelAttachRequest(const TBTDevAddr& aBTDevice)
+	{
+	LOG_FUNC
+	
+	iLock.Wait();
+	TInt err = KErrFirstChannelNotAttached;
+	const TAvctpRemoteDeviceInfo* info = RemoteDeviceInfo(aBTDevice);
+	if (info)
+		{
+		err = KErrRepeatConnectionAttempt;
+		if (!info->HasSecondChannel())
+			{
+			__ASSERT_DEBUG(iSecondaryChannelNotify, Panic(EAvctpSecondChannelNotPresent));
+			if (iState == EListening)
+				{
+				iState = ECreatingSecondLink;
+				iSecondaryChannelController->AttachRequest(aBTDevice);
+				err = KErrNone;
+				}
+			}
+		}
+	iLock.Signal();
+	return err;
+	}
+
+TInt CAvctpRemoteDevices::SecondaryChannelDetachRequest(const TBTDevAddr& aBTDevice)
+	{
+	LOG_FUNC
+	
+	iLock.Wait();
+	TInt err = KErrSecondChannelNotAttached;
+	const TAvctpRemoteDeviceInfo* info = RemoteDeviceInfo(aBTDevice);
+	if (info)
+		{
+		if (info->HasSecondChannel())
+			{
+			__ASSERT_DEBUG(iSecondaryChannelNotify, Panic(EAvctpSecondChannelNotPresent));
+			iSecondaryChannelController->DetachRequest(aBTDevice);
+			err = KErrNone;
+			}
+		}
+	iLock.Signal();
+	return err;
+	}
+
+void CAvctpRemoteDevices::SecondaryChannelCancelAttach(const TBTDevAddr& aBTDevice)
+	{
+	LOG_FUNC
+	
+	iLock.Wait();
+	
+	if (IsAttached(aBTDevice) && iState == ECreatingSecondLink)
+		{
+		// the notifications are only propagated if the state is indicating an
+		// outstanding request. So, simply changing the state to listening will avoid
+		// the notification to be propagated.
+		iState = EListening;	
+		}
+	
+	iLock.Signal();
+	}
+
+void CAvctpRemoteDevices::RemoveRemoteDevice(const TBTDevAddr& aBTDevice)
+	{
+	LOG_FUNC
+	
+	__DEBUG_ONLY
+		(
+		TBuf<KBTAddressLength> address;
+		aBTDevice.GetReadable(address);
+		)
+	
+	LOG1(_L("BT Device 0x%S"), &address);
+
+	TInt index;	  
+	for(index= 0; index < iRemoteAddrs.Count(); index++)
+		{
+		if (iRemoteAddrs[index].RemoteAddress() == aBTDevice)
+			{
+			iRemoteAddrs.Remove(index);
+			break; // since there should only be one remote device per TBTDevAddr
+			}
+		}
+	}
+
+const TAvctpRemoteDeviceInfo* CAvctpRemoteDevices::RemoteDeviceInfo(const TBTDevAddr& aBTDevice) const
+	{
+	LOG_FUNC
+
+	const TAvctpRemoteDeviceInfo* deviceInfo = NULL;
+	
+	TInt index;
+	for (index = 0; index < iRemoteAddrs.Count(); index++)
+		{
+		if (iRemoteAddrs[index].RemoteAddress() == aBTDevice)
+			{
+			deviceInfo = &iRemoteAddrs[index];
+			break; // there should only be one remote device on each TBTDevAddr
+			}
+		}
+	
+	return deviceInfo;
+	}
+
+TBool CAvctpRemoteDevices::IsAttached(const TBTDevAddr& aBTDevice) const
+	{
+	LOG_FUNC
+	
+	iLock.Wait();
+	
+	TBool ans = RemoteDeviceInfo(aBTDevice) ? ETrue : EFalse;
+	
+#ifdef _DEBUG
+//	// Check there is only one remote device on this TBTDevAddr in the array
+	TInt numDevices = 0;
+	TInt index;
+	for (index = 0; index < iRemoteAddrs.Count(); index++)
+		{
+		if (iRemoteAddrs[index].RemoteAddress() == aBTDevice)
+			{
+			numDevices++;
+			}
+		}
+	__ASSERT_DEBUG(((ans && numDevices == 1) || (!ans && numDevices == 0)), Panic(EAvctpRemoteAddressOccursMultipleTimes));
+#endif // _DEBUG
+	
+	LOG1(_L("IsAttached() result is: %d"), ans)
+	
+	iLock.Signal();
+	
+	return ans;
+	}
+
+/**
+All Errors to do with a remote device will result in a disconnect Ind happening
+because all device errors are currently fatal. NB since the actual error doesn't
+matter to the client they don't get informed of it.
+*/
+void CAvctpRemoteDevices::NotifyError(const TBTDevAddr& aBTDevice, TInt aError, TInt aChannel)
+	{
+	LOG_FUNC
+	
+	MAvctpEventNotify* notify = aChannel == KAvctpPrimaryChannel ? &iNotify : iSecondaryChannelNotify; 
+	notify->MaenErrorNotify(aBTDevice, aError);	
+	// don't do anything here as the client may have closed the RAvctp object after the NotifyError
+	}
+
+void CAvctpRemoteDevices::SetSecondaryChannelNotifyL(MAvctpEventNotify* aSecondChannelNotify)
+	{
+	LOG_FUNC
+	
+	iLock.Wait();
+	
+	__ASSERT_DEBUG(!aSecondChannelNotify || !iSecondaryChannelNotify, Panic(EAvctpSecondaryChannelNotifyAlreadyAssigned));
+
+	if(aSecondChannelNotify)
+		{
+		iSecondaryChannelNotify = aSecondChannelNotify;
+		
+		iSecondaryChannelController = CSecondaryChannelController::NewL(*this, iSocketServ, iPid);
+		iSecondaryChannelController->Listen();
+		}
+	else
+		{
+		delete iSecondaryChannelController;
+		iSecondaryChannelController = NULL;
+		}
+	
+	iLock.Signal();
+	}
+
+void CAvctpRemoteDevices::PrimaryChannelAttachConfirm(const TBTDevAddr& aAddr, TInt aMtu, TInt aError)
+	{
+	LOG_FUNC
+	
+	iLock.Wait();
+	
+	// state must be:
+	// ECreatingControlLink because we have requested to be attached.
+	// EListening because we had asked to be attached but then we canceled the request.
+	__ASSERT_DEBUG(iState == ECreatingControlLink || iState == EListening, Panic(EAvctpInvalidChannelState));
+	
+	if (iState == ECreatingControlLink)
+		{
+		iNotify.MaenAttachConfirm(aAddr, aMtu, aError);
+		if (aError == KErrNone && !IsAttached(aAddr))
+			{
+			TAvctpRemoteDeviceInfo info(aAddr);
+			aError = iRemoteAddrs.Append(info);
+			}
+		}
+	
+	iState = EListening;
+	iPrimaryChannelController->Listen();
+	
+	iLock.Signal();
+	}
+
+void CAvctpRemoteDevices::PrimaryChannelAttachIndicate(const TBTDevAddr& aAddr, TInt aMtu)
+	{
+	LOG_FUNC
+	
+	iLock.Wait();
+	
+	TBool clientAccepts = EFalse;
+	iNotify.MaenAttachIndicate(aAddr, aMtu, clientAccepts);
+	TInt err = PropagateAttachRsp(aAddr, clientAccepts, KAvctpPrimaryChannel);
+	// we can have an error if the array append fails 
+	if (err == KErrNone)
+		{
+		if (clientAccepts)
+			{
+			iPrimaryChannelController->AgreeAttachment(aAddr);
+			// iSecondaryChannelController->AttachPassively(aAddr);
+			}
+		}
+	else 
+		{
+		NotifyError(aAddr, err, KAvctpPrimaryChannel);
+		}
+
+	// whatever happen (even an error) we need to listen anyway.
+	iState = EListening;
+	iPrimaryChannelController->Listen();
+	
+	iLock.Signal();
+	}
+
+void CAvctpRemoteDevices::PrimaryChannelDetachConfirm(const TBTDevAddr& aAddr, TInt aError)
+	{
+	LOG_FUNC
+	
+	iLock.Wait();
+	
+	if (aError == KErrNone)
+		{
+		const TAvctpRemoteDeviceInfo* info = RemoteDeviceInfo(aAddr);
+		__ASSERT_DEBUG(info, Panic(EAvctpRemoteDeviceNotConnected));
+		
+		RemoveRemoteDevice(aAddr);
+		iNotify.MaenDetachConfirm(aAddr, KErrNone);
+		}
+	else
+		{
+		NotifyError(aAddr, aError, KAvctpPrimaryChannel);
+		}
+	
+	// whatever happens we need to listen
+	iState = EListening;
+	iPrimaryChannelController->Listen();
+	
+	iLock.Signal();
+	}
+
+void CAvctpRemoteDevices::PrimaryChannelDetachIndicate(const TBTDevAddr& aAddr)
+	{
+	LOG_FUNC
+	
+	iLock.Wait();
+	
+	// it can happen that we are in ECreatingSecondLink and the remote disconnects
+	// the first channel.
+	// server side we first notify the secondary channel, then the first one. 
+	// But we are not guarantee that the secondary channel will be served first
+	// so we need to save the oldstate to update after this process.
+	// in this case, when we'll receive the secondary channel event we are in the 
+	// correct state.
+	// even if the state at the end won't be listening, we have to listen on the 
+	// primary channel anyway.
+	
+	TState oldState = iState;
+	
+    const TAvctpRemoteDeviceInfo* info = RemoteDeviceInfo(aAddr);
+    __ASSERT_DEBUG(info, Panic(EAvctpRemoteDeviceNotConnected));
+
+    RemoveRemoteDevice(aAddr);
+    iNotify.MaenDetachIndicate(aAddr);
+	
+	// we go to the previous state (before detaching the primary channel). see the comment above
+	// for more info
+	iState = oldState;
+	// but we need to listen on the primary channel anyway.
+	iPrimaryChannelController->Listen();
+	
+	iLock.Signal();
+	}
+
+void CAvctpRemoteDevices::PrimaryChannelIoctlError(const TBTDevAddr& aAddr, TInt aError)
+	{
+	LOG_FUNC
+	
+	iLock.Wait();
+	
+	iNotify.MaenErrorNotify(aAddr, aError);
+	
+	// after having notified the error we must back to listen, despite the state we were before.
+	iState = EListening;
+	iPrimaryChannelController->Listen();
+	
+	iLock.Signal();
+	}
+
+void CAvctpRemoteDevices::PrimaryChannelAgreementError(const TBTDevAddr& aAddr, TInt __DEBUG_ONLY(aError))
+	{
+	LOG_FUNC
+	
+	iLock.Wait();
+	
+	const TAvctpRemoteDeviceInfo* info = RemoteDeviceInfo(aAddr);
+	__ASSERT_DEBUG(info, Panic(EAvctpRemoteDeviceNotConnected));
+	__ASSERT_DEBUG(!info->HasSecondChannel(), Panic(EAvctpSecondaryChannelUnexpected));
+	__ASSERT_DEBUG(aError == KErrMuxerNotFound || aError == KErrNoMemory, Panic(EAvctpUnexpectedErrorCode));
+	
+	RemoveRemoteDevice(aAddr);
+	iNotify.MaenDetachIndicate(aAddr);
+	iState = EListening;
+	iPrimaryChannelController->Listen();
+	
+	iLock.Signal();
+	}
+
+void CAvctpRemoteDevices::SecondaryChannelAttachConfirm(const TBTDevAddr& aAddr, TInt aMtu, TInt aError)
+	{
+	LOG_FUNC
+	
+	iLock.Wait();
+	
+	// state must be:
+	// ECreatingControlLink because we have requested to be attached.
+	// EListening because we had asked to be attached but then we canceled the request.
+	__ASSERT_DEBUG(iState == ECreatingSecondLink || iState == EListening, Panic(EAvctpInvalidChannelState));
+		
+	if (iState == ECreatingSecondLink)
+		{
+		iSecondaryChannelNotify->MaenAttachConfirm(aAddr, aMtu, aError);
+		if (aError == KErrNone)
+			{
+			TAvctpRemoteDeviceInfo* info = const_cast<TAvctpRemoteDeviceInfo*>(RemoteDeviceInfo(aAddr));
+			__ASSERT_DEBUG(info, Panic(EAvctpRemoteDeviceNotConnected));
+			info->SetHasSecondChannel(ETrue);
+			}
+		else
+			{
+			if (aError == KErrMuxerShutDown)
+				{
+				RemoveRemoteDevice(aAddr);
+				}
+			}
+		}
+	iState = EListening;
+	iSecondaryChannelController->Listen();
+	
+	iLock.Signal();
+	}
+
+void CAvctpRemoteDevices::SecondaryChannelAttachIndicate(const TBTDevAddr& aAddr, TInt aMtu)
+	{
+	LOG_FUNC
+	
+	iLock.Wait();
+	
+	TAvctpRemoteDeviceInfo* info = const_cast<TAvctpRemoteDeviceInfo*>(RemoteDeviceInfo(aAddr));
+
+	// if info is NULL it probably means that the first channel attach indication was refused or
+	// the attach request was cancelled. However, in that case, we do nothing.
+	if (info)
+		{
+		TBool clientAccepts = EFalse;
+		if(iSecondaryChannelNotify)
+			{
+			iSecondaryChannelNotify->MaenAttachIndicate(aAddr, aMtu, clientAccepts);
+	#ifdef _DEBUG		
+			TInt err = 
+	#endif
+			PropagateAttachRsp(aAddr, clientAccepts, KAvctpSecondaryChannel);	
+			
+			// err must always be KErrNone when PropagateConnectRsp is called on the secondary channel
+			__ASSERT_DEBUG(err == KErrNone, Panic(EAvctpUnexpectedErrorCode));
+			
+			if (clientAccepts)
+				{
+				iSecondaryChannelController->AgreeAttachment(aAddr);
+				info->SetHasSecondChannel(ETrue);
+				}
+			}
+		else // No-one's interested
+			{
+			iSecondaryChannelController->RefuseAttach(aAddr);
+			}
+		}
+	iState = EListening;
+	iSecondaryChannelController->Listen();
+	
+	iLock.Signal();
+	}
+
+void CAvctpRemoteDevices::SecondaryChannelDetachConfirm(const TBTDevAddr& aAddr, TInt aError)
+	{
+	LOG_FUNC
+	
+	iLock.Wait();
+	
+	if (aError == KErrNone)
+		{
+		__ASSERT_DEBUG(iSecondaryChannelNotify, Panic(EAvctpSecondChannelNotPresent));
+		
+		TAvctpRemoteDeviceInfo* info = const_cast<TAvctpRemoteDeviceInfo*>(RemoteDeviceInfo(aAddr));
+		__ASSERT_DEBUG(info, Panic(EAvctpRemoteDeviceNotConnected));
+		__ASSERT_DEBUG(info->HasSecondChannel(), Panic(EAvctpSecondChannelNotPresent));
+		iSecondaryChannelNotify->MaenDetachConfirm(aAddr, KErrNone);
+		info->SetHasSecondChannel(EFalse);
+		}
+	else
+		{
+		NotifyError(aAddr, aError, KAvctpSecondaryChannel);
+		}
+
+	iState = EListening;
+	iSecondaryChannelController->Listen();
+	
+	iLock.Signal();
+	}
+
+void CAvctpRemoteDevices::SecondaryChannelDetachIndicate(const TBTDevAddr& aAddr)
+	{
+	LOG_FUNC
+	
+	iLock.Wait();
+	
+	// In the case where we haven't got a secondary channel notify we may still get
+	// a detach indicate if the refusal for the attach indicate has not been 
+	// processed before the remote disconnects.  
+	if(iSecondaryChannelNotify)
+		{
+		iSecondaryChannelNotify->MaenDetachIndicate(aAddr);
+		TAvctpRemoteDeviceInfo* info = const_cast<TAvctpRemoteDeviceInfo*>(RemoteDeviceInfo(aAddr));
+        if (info && info->HasSecondChannel())
+            {
+            info->SetHasSecondChannel(EFalse);
+            }
+		}
+#ifdef _DEBUG
+	else
+		{
+		// in this case we should not have any remote device info - check
+		TAvctpRemoteDeviceInfo* info = const_cast<TAvctpRemoteDeviceInfo*>(RemoteDeviceInfo(aAddr));
+		__ASSERT_ALWAYS(!info || !info->HasSecondChannel(), Panic(EDetachIndicateForSecondChannelWithNoConsumer));
+		}
+#endif
+	
+	iState = EListening;
+	iSecondaryChannelController->Listen();
+	
+	iLock.Signal();
+	}
+
+void CAvctpRemoteDevices::SecondaryChannelIoctlError(const TBTDevAddr& aAddr, TInt aError)
+	{
+	LOG_FUNC
+	
+	iLock.Wait();
+	
+	__ASSERT_DEBUG(iSecondaryChannelNotify, Panic(EAvctpSecondChannelNotPresent));
+	iSecondaryChannelNotify->MaenErrorNotify(aAddr, aError);
+	// after having notified the error we must back to listen, despite the state we were before.
+	iState = EListening;
+	iSecondaryChannelController->Listen();
+	
+	iLock.Signal();
+	}
+
+void CAvctpRemoteDevices::SecondaryChannelAgreementError(const TBTDevAddr& aAddr, TInt __DEBUG_ONLY(aError))
+	{
+	LOG_FUNC
+	
+	iLock.Wait();
+	
+	TAvctpRemoteDeviceInfo* info = const_cast<TAvctpRemoteDeviceInfo*>(RemoteDeviceInfo(aAddr));
+	__ASSERT_DEBUG(info, Panic(EAvctpRemoteDeviceNotConnected));
+	__ASSERT_DEBUG(info->HasSecondChannel(), Panic(EAvctpSecondaryChannelUnexpected));
+	__ASSERT_DEBUG(aError == KErrMuxerNotFound, Panic(EAvctpUnexpectedErrorCode));
+	
+	RemoveRemoteDevice(aAddr);
+	iSecondaryChannelNotify->MaenDetachIndicate(aAddr);
+	iState = EListening;
+	iSecondaryChannelController->Listen();
+	
+	iLock.Signal();
+	}