ImagePrint/ImagePrintEngine/DeviceProtocols/btprotocol/src/cbtdiscover.cpp
branchRCL_3
changeset 28 d59c248c9d36
parent 0 d11fb78c4374
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ImagePrint/ImagePrintEngine/DeviceProtocols/btprotocol/src/cbtdiscover.cpp	Wed Sep 01 12:30:38 2010 +0100
@@ -0,0 +1,895 @@
+/*
+* Copyright (c) 2004-2007 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:  Contains the CBtDiscover class definition. Bluetooth device discovery and SDP.
+*
+*/
+
+
+#include "cbtdiscover.h"
+#include "clog.h"
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+/**
+ * @brief Instantiate new CBtDiscover object.
+ */
+CBtDiscover* CBtDiscover::NewL()
+	{
+	CBtDiscover* self = new (ELeave) CBtDiscover();
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(); // self
+	return self;
+	}
+
+
+/**
+ * @brief Two phase construction.
+ */
+void CBtDiscover::ConstructL()
+	{
+	iSdpSearchPattern = CSdpSearchPattern::NewL();
+	iMatchList = CSdpAttrIdMatchList::NewL();
+	}
+
+/**
+ * @brief Constructor.
+ */
+CBtDiscover::CBtDiscover() :
+	iCurrentSDPAttrID(0),
+	iCurrentServiceUUID(0),
+	iFoundSTS(EFalse),
+	iWantedService(ETrue),
+	iDoingSDP(EFalse),
+	iFindingDevices(EFalse),
+	iAgent(NULL),
+	iRequestStatus(NULL),
+	iSdpSearchPattern(NULL),
+	iMatchList(NULL),
+	iContentTypes(NULL),
+	iPrinterModel(NULL),
+	iBPPPort(0),
+	iSTSPort(0),
+	iOPPPort(0)
+	{
+	}
+
+/**
+ * @brief Destructor.
+ */
+CBtDiscover::~CBtDiscover()
+	{
+	LOG("CBtDiscover::~CBtDiscover begin");
+	Stop();
+	if(iSdpSearchPattern)
+		delete iSdpSearchPattern;
+	if(iMatchList)
+		delete iMatchList;
+	if(iContentTypes)
+		delete iContentTypes;
+	if(iPrinterModel)
+		delete iPrinterModel;
+	if(iAgent)
+		delete iAgent;
+	LOG("CBtDiscover::~CBtDiscover end");
+	}
+
+/**
+ * @brief Destructor.
+ */
+void CBtDiscover::Reset()
+	{
+	LOG("CBtDiscover::Reset begin");
+
+	StopSDP(); // resets also public member variables and iDoingSDP flag
+
+	iBPPPort = 0;
+	iSTSPort = 0;
+	iOPPPort = 0;
+
+	if(iPrinterModel)
+		delete iPrinterModel;
+	iPrinterModel = NULL;
+
+	if(iSdpSearchPattern)
+		iSdpSearchPattern->Reset();
+	
+	// No can do if remove fails -> ignore leave
+	if(iMatchList)
+		TRAP_IGNORE( iMatchList->RemoveL(TAttrRange(0x0000, 0xFFFF)) );
+	
+	if(iContentTypes)
+		delete iContentTypes;
+	iContentTypes = NULL;
+	
+
+	if( iRequestStatus )
+	{
+		if(KRequestPending == iRequestStatus->Int())
+			User::RequestComplete(iRequestStatus, KErrNone);
+	}
+	iRequestStatus = NULL;
+
+	LOG("CBtDiscover::Reset end");
+	}
+
+/**
+  * @brief Starts the asynchronous search for Bluetooth devices.
+  *
+  * Starts the asynchronous search for Bluetooth devices. The Active Object containing
+  * this object, CRsBtDiscoverEngine, is notified after the first device is found.
+  * @param aStatus Status variable from CRsBtDiscoverEngine, receives notification when asynchronous call completes.
+  */
+void CBtDiscover::Start(TRequestStatus &aStatus)
+	{
+	LOG("[CBtDiscover::Start]\t begin");
+	aStatus = KErrNotSupported;
+
+	TInt err = iSocketServ.Connect();
+	LOG1("[CBtDiscover::Start]\t iSocketServ.Connect() err: %d", err);
+	if( KErrNone == err )
+	{
+		err = iHostResolver.Open( iSocketServ, KBTAddrFamily, KBTLinkManager );
+		LOG1("[CBtDiscover::Start]\t iHostResolver.Open() err: %d", err);
+	}
+
+	if( KErrNone == err )
+	{
+		iFindingDevices = ETrue;
+		iSockAddr.SetIAC( KGIAC );
+		iSockAddr.SetAction( KHostResInquiry | KHostResName | KHostResIgnoreCache );
+		iHostResolver.GetByAddress( iSockAddr, iNameEntry, aStatus );
+	}
+	else
+	{
+		iFindingDevices = EFalse;
+		iSocketServ.Close();
+		iHostResolver.Close();
+	}
+
+	LOG("[CBtDiscover::Start]\t end");
+	}
+
+/**
+  * @brief Continues the asynchronous search for Bluetooth devices.
+  *
+  * Continues the asynchronous search for Bluetooth devices. The Active Object containing
+  * this object, CRsBtDiscoverEngine, is notified after the next device is found.
+  * @param aStatus Status variable from CRsBtDiscoverEngine, receives notification when asynchronous call completes.
+  */
+void CBtDiscover::GetNextDevice(TRequestStatus &aStatus)
+	{
+	LOG("[CBtDiscover::GetNextDevice]\t");
+	iHostResolver.Next(iNameEntry, aStatus);
+	}
+
+/**
+  * @brief Stop the asynchronous search for Bluetooth devices.
+  */
+void CBtDiscover::Stop()
+	{
+	LOG1("[CBtDiscover::Stop]\t begin with iFindingDevices: %d", iFindingDevices);
+
+	if(iFindingDevices)
+		iHostResolver.Cancel();
+	iFindingDevices = EFalse;
+
+	Reset();
+
+	iHostResolver.Close();
+	iSocketServ.Close();
+
+	LOG("[CBtDiscover::Stop]\t end");
+	}
+
+/**
+  * @brief Is an asynchronous search for Bluetooth devices in progress?
+  * @return True, a search is in progress, or False, no search is in progress
+  */
+TBool CBtDiscover::IsFindingDevices() const
+	{
+	return iFindingDevices;
+	}
+
+/**
+  * @brief Get the last Bluetooth device found by the search.
+  * @return The last Bluetooth device found by the search.
+  */
+TNameRecord CBtDiscover::GetNameRecord() const
+	{
+	return iNameEntry();
+	}
+
+/**
+  * @brief Store Bluetooth device information, in preparation for doing an SDP query.
+  */
+void CBtDiscover::SetNameRecord(TNameRecord aNameRecord)
+	{
+	TNameEntry nameEntry(aNameRecord);
+	iNameEntry = nameEntry;
+	}
+
+/**
+  * @brief Starts the asynchronous request for the SDP record.
+  *
+  * Starts the asynchronous request for the SDP record from a Bluetooth device.
+  * Specifically the device is tested for the BPP and OPP services.
+  * @param aStatus Status variable from CRsBtDiscoverEngine, receives notification when asynchronous call completes.
+  * @param aServiceMask Bitmask to require (only) certain services. All marked by default.
+  */
+void CBtDiscover::GetSupportedServicesL(TRequestStatus &aStatus, TUint32 aServiceMask)
+	{
+	LOG("[CBtDiscover::GetSupportedServicesL]\t begin");
+
+	Reset();
+	iRequestStatus = &aStatus;
+	*iRequestStatus = KRequestPending;	
+
+	iMatchList->RemoveL(TAttrRange(0x0000, 0xFFFF));
+	iMatchList->AddL(TAttrRange(KSdpAttrIdServiceClassIDList));
+	iMatchList->AddL(TAttrRange(KSdpAttrIdProtocolDescriptorList));
+	iMatchList->AddL(TAttrRange(KSdpAttrIdAdditionalProtocolDescriptorLists));
+	iMatchList->AddL(TAttrRange(KSdpAttrIdDocFormatsSupported));
+	iMatchList->AddL(TAttrRange(KSdpAttrIdModelID));
+	iMatchList->AddL(TAttrRange(KSdpAttrIdMaxWidth));
+	iMatchList->AddL(TAttrRange(KSdpAttrIdMaxLen));
+
+	iSdpSearchPattern->Reset();
+
+	if(KBtMaskObjectPush & aServiceMask)
+		iSdpSearchPattern->AddL(KBTSDPOBEXObjectPush); // 0x1105
+	if(KBtMaskDirectPrinting & aServiceMask)
+		iSdpSearchPattern->AddL(KBTSDPDirectPrinting); // 0x1118
+	if(KBtMaskPrintingStatus & aServiceMask)
+		iSdpSearchPattern->AddL(KBTSDPPrintingStatus); // 0x1123
+	if(KBtMaskL2CAP & aServiceMask)
+		iSdpSearchPattern->AddL(KBTSDPL2CAP);
+	if(KBtMaskRFCOMM & aServiceMask)
+		iSdpSearchPattern->AddL(KBTSDPRFCOMM);
+	if(KBtMaskObex & aServiceMask)
+		iSdpSearchPattern->AddL(KBTSDPObex);
+
+	StartSDPL();
+
+	LOG("[CBtDiscover::GetSupportedServicesL]\t end");
+	}
+
+/**
+  * @brief Initiate the SDP request started in VerifyServiceSupportL.
+  */
+void CBtDiscover::StartSDPL()
+	{
+	StopSDP();
+	TBTDevAddr btAddr = ((TInquirySockAddr)iNameEntry().iAddr).BTAddr();
+	iAgent = CSdpAgent::NewL(*this, btAddr);
+	iAgent->SetRecordFilterL(*iSdpSearchPattern);
+	iAgent->SetAttributePredictorListL(*iMatchList);
+	iAgent->NextRecordRequestL();
+	iDoingSDP = ETrue;
+	}
+
+/**
+  * @brief Stop the asynchronous request for the SDP record from a Bluetooth device.
+  */
+void CBtDiscover::StopSDP()
+	{
+	LOG1("[CBtDiscover::StopSDP] begin with iAgent address: %d", TInt(iAgent));
+	iDoingSDP = EFalse;
+
+	iCurrentSDPAttrID = 0;
+    iCurrentServiceUUID = 0;
+	iFoundSTS = EFalse;
+	iWantedService = ETrue;
+
+	if( iAgent )
+		{
+		iAgent->Cancel();
+		delete iAgent;
+		iAgent = NULL;
+		}
+	LOG("[CBtDiscover::StopSDP] end");
+	}
+
+/**
+  * @brief Is an asynchronous request for the SDP record in progress?.
+  *
+  * Is an asynchronous request for the SDP record from a Bluetooth device in progress?
+  * @return True, a request is in progress, or False, no request is in progress
+  */
+TBool CBtDiscover::IsDoingSDP() const
+	{
+	return iDoingSDP;
+	}
+
+/**
+  * The CSdpAgent object calls this to notify that the attributes from an SDP record are ready for parsing.
+  * @param aAttrValue Structured attribute(s) of SDP record, parsed with TBTAttribValVisitor
+  */
+void CBtDiscover::AttributeRequestResult(TSdpServRecordHandle /*aHandle*/, TSdpAttributeID aAttrID, CSdpAttrValue *aAttrValue)
+	{
+	TRAPD( err, AttributeRequestResultL( aAttrID, aAttrValue ) );
+	if(KErrNone != err)
+		LOG1("[CBtDiscover::AttributeRequestResult]\t AttributeRequestResultL leaves with: %d", err);
+	}
+
+void CBtDiscover::AttributeRequestResultL( TSdpAttributeID aAttrID, CSdpAttrValue* aValue )
+	{
+	LOG1("[CBtDiscover::AttributeRequestResultL]\t *** Device: %S: ", &GetDeviceName() );
+	LOG2("CBtDiscover::AttributeRequestResultL aAttrID: %d, aValue->Type(): %d", aAttrID, aValue->Type());
+
+	iCurrentSDPAttrID = aAttrID;
+	CBTAttribValVisitor* valVisitor = NULL;
+
+	switch( aAttrID )
+		{
+		case KSdpAttrIdServiceClassIDList:
+			iCurrentServiceUUID = 0; // reset
+		case KSdpAttrIdProtocolDescriptorList:
+		case KSdpAttrIdAdditionalProtocolDescriptorLists:
+		case KSdpAttrIdDocFormatsSupported:
+		case KSdpAttrIdModelID:
+		case KSdpAttrIdMaxWidth:
+		case KSdpAttrIdMaxLen:
+			valVisitor = CBTAttribValVisitor::NewLC( *this );
+			aValue->AcceptVisitorL( *valVisitor ); // goes in CBTAttribValVisitor::VisitAttributeValueL 
+			CleanupStack::PopAndDestroy( valVisitor );
+			break;
+		default:
+			break;
+		}
+
+	delete aValue;
+
+	LOG("CBtDiscover::AttributeRequestResultL end");
+	}
+
+
+/**
+  * The CSdpAgent object calls this to notify that the SDP record request has been completed.
+  */
+void CBtDiscover::NextRecordRequestComplete(TInt aError, TSdpServRecordHandle aHandle, TInt aTotalRecordsCount)
+	{
+	LOG1("CBtDiscover::NextRecordRequestComplete aError: %d", aError);
+	LOG1("CBtDiscover::NextRecordRequestComplete aHandle: %d", aHandle);
+	LOG1("CBtDiscover::NextRecordRequestComplete aTotalRecordsCount: %d", aTotalRecordsCount);
+	TInt err( aError );
+
+	if( KErrNone == aError  && aTotalRecordsCount > 0)
+	{
+		TRAP( err, iAgent->AttributeRequestL( aHandle, KSdpAttrIdServiceClassIDList ) );
+		LOG1("CBtDiscover::NextRecordRequestComplete err: %d", err);
+	}
+
+	if( KErrNone != err || !aTotalRecordsCount )
+	{
+		if( KErrEof == err || !aTotalRecordsCount )
+		{
+			err = KErrNone;
+		}
+		StopSDP();
+		LOG("CBtDiscover::NextRecordRequestComplete User::RequestComplete");
+		User::RequestComplete( iRequestStatus, err );	
+	}
+
+	// reset
+	iCurrentServiceUUID = 0; 
+	iWantedService = ETrue;
+	iCurrentSDPAttrID = 0;
+	LOG("CBtDiscover::NextRecordRequestComplete end");
+	}
+
+/**
+  * The CSdpAgent object calls this to notify that the current SDP record has no more attributes.
+  * @param aError Standard Symbian error code, KErrNone indicates success.
+  */
+void CBtDiscover::AttributeRequestComplete(TSdpServRecordHandle aHandle, TInt aError)
+{
+	LOG("[CBtDiscover::AttributeRequestComplete]\t Begin.");
+	if( KErrNone != aError || !iWantedService || 0 == iCurrentSDPAttrID)
+	{
+		LOG1("[CBtDiscover::AttributeRequestComplete]\t err: %d", aError);
+		iCurrentSDPAttrID = 0;
+		LOG("[CBtDiscover::AttributeRequestComplete]\t Not wanted service. Get next record.");
+		TRAPD( err, iAgent->NextRecordRequestL() );
+		if(KErrNone != err)
+		{
+			NextRecordRequestComplete(err, aHandle, 0);
+		}
+	}
+	else
+	{
+		LOG("[CBtDiscover::AttributeRequestComplete]\t Wanted service. Get next attribute.");
+		TRAPD( err, GetNextAttributeL(aHandle) );
+		if( KErrNone != err )
+			LOG1("[CBtDiscover::AttributeRequestComplete]\t err: %d", err);
+	}
+	iCurrentSDPAttrID = 0;
+	LOG("[CBtDiscover::AttributeRequestComplete]\t End.");
+}
+
+/**
+  * @brief Get BPP port.
+  * @return Port (i.e. Bluetooth channel) of the BPP service on the printer. Returns -1 if port is unknown.
+  */
+TInt CBtDiscover::GetBPPPort() const
+	{
+	return iBPPPort;
+	}
+
+/**
+  * @brief Get Status port.
+  * @return Port (i.e. Bluetooth channel) of the status service on the printer. Returns -1 if port is unknown.
+  */
+TInt CBtDiscover::GetSTSPort() const
+	{
+	return iSTSPort;
+	}
+
+/**
+  * @brief Get OPP port.
+  * @return Port (i.e. Bluetooth channel) of the OPP service on the printer. Returns -1 if port is unknown.
+  */
+TInt CBtDiscover::GetOPPPort() const
+	{
+	return iOPPPort;
+	}
+
+/**
+  * @brief Set the BPP Port.
+  *
+  * Called from TBTAttribValVisitor to set the Port (i.e. Bluetooth channel).
+  * Also used to indicate that the BPP service was successfully detected in the SDP response.
+  */
+void CBtDiscover::SetBPPPort(TInt aPort)
+	{
+	LOG1("CBtDiscover::SetBPPPort aPort: %d", aPort);
+	iBPPPort = aPort;
+	}
+
+/**
+  * @brief Set the Status Port.
+  *
+  * Called from TBTAttribValVisitor to set the Port (i.e. Bluetooth channel).
+  * Also used to indicate that the Status service was successfully detected in the SDP response.
+  */
+void CBtDiscover::SetSTSPort(TInt aPort)
+	{
+	LOG1("CBtDiscover::SetSTSPort aPort: %d", aPort);
+	iSTSPort = aPort;
+	}
+/**
+  * @brief Set the OPP Port.
+  *
+  * Called from TBTAttribValVisitor to set the Port (i.e. Bluetooth channel).
+  * Also used to indicate that the OPP service was successfully detected in the SDP response.
+  */
+void CBtDiscover::SetOPPPort(TInt aPort)
+	{
+	LOG1("CBtDiscover::SetOPPPort aPort: %d", aPort);
+	iOPPPort = aPort;
+	}
+
+/**
+  * @brief Bluetooth device supports BPP?.
+  *
+  * Does the SDP response for the current Bluetooth device indicate that BPP is supported?
+  * Checks to make sure that a Port (i.e. Bluetooth channel) for the BPP service was determined,
+  * AND that the device supports the Mime/Multiplex document type.
+  * @return True, BPP is supported, or False, BPP is not supported
+  */
+TBool CBtDiscover::IsBPPSupported() const
+	{
+	_LIT(KMimeXhtmlType, "application/vnd.pwg-xhtml-print+xml");
+	_LIT(KMimeMultiplexType, "application/vnd.pwg-multiplexed");
+	LOG1("CBtDiscover::IsBPPSupported iBPPPort: %d", iBPPPort);
+	return ( iBPPPort && (SupportsContentType( KMimeMultiplexType ) || SupportsContentType( KMimeXhtmlType )) );
+	}
+
+/**
+  * @brief Bluetooth device supports OPP?.
+  *
+  * Does the SDP response for the current Bluetooth device indicate that OPP is supported?
+  * @return True if OPP is supported. False if OPP is not supported.
+  */
+TBool CBtDiscover::IsOPPSupported() const
+	{
+	LOG1("CBtDiscover::IsOPPSupported iOPPPort: %d", iOPPPort);
+	if(iOPPPort)
+		return ETrue;
+	else
+		return iOPPPort;
+	}
+
+/**
+  * @brief Obtain Bluetooth device class.
+  *
+  * @return Bluetooth device class. Used by CRsBtDiscoverEngine to do the first check
+  * whether the current Bluetooth device is likely to support BPP or OPP.
+  * @sa KBTMinDevClassPrinter
+  */
+TBTDeviceClass CBtDiscover::GetDeviceClass() const
+	{
+	TInquirySockAddr sockAddr = (TInquirySockAddr)iNameEntry().iAddr;
+	TBTDeviceClass devClass(sockAddr.MajorServiceClass(),
+		sockAddr.MajorClassOfDevice(),
+		sockAddr.MinorClassOfDevice());
+
+	return devClass;
+	}
+
+/**
+  * @brief Obtain Bluetooth device name.
+  *
+  * @return Friendly name of current Bluetooth device.
+  */
+const TDesC& CBtDiscover::GetDeviceName() const
+	{
+	return iNameEntry().iName;
+	}
+
+/**
+  * @brief Sets supported content types.
+  *
+  * Called from TBTAttribValVisitor to set the supported content types.
+  * @param aString String of comma separated mime-types.
+  */
+void CBtDiscover::SetContentTypesL(const TPtrC8 &aString)
+	{
+#ifdef _DEBUG
+
+	LOG1("CBtDiscover::SetContentTypesL %d", aString.Length());
+	
+	TInt start = 0;
+	TInt end = aString.Locate(',');
+	TPtrC8 ptr;
+	while(end > 0 && start <= aString.Length())
+	{
+		ptr.Set(aString.Mid(start, end));
+		LOG81("[CBtDiscover::SetContentTypesL]\t ptr: \"%S\"", &ptr);
+		++end;
+		start += end;
+		if(start > aString.Length())
+			break;
+		
+		TPtrC8 left = aString.Mid(start);
+		end = left.Locate(',');
+		if(KErrNotFound == end)
+			end = aString.Length() - start;
+	}
+
+#endif
+	delete iContentTypes;
+	iContentTypes = NULL;
+	iContentTypes = HBufC::NewL(aString.Length());
+	TPtr bufPtr(iContentTypes->Des());
+	bufPtr.Copy(aString);
+	}
+
+/**
+  * @brief Ask if device supports a content type.
+  *
+  * Searches the string of supported content types found in the SDP record of the current Bluetooth device.
+  *
+  * @param aContType Mime-type to search for.
+  *
+  * @return True, if the passed in content type was found, False, if it was not found
+  */
+TBool CBtDiscover::SupportsContentType(const TDesC &aContType) const
+	{
+	LOG1("CBtDiscover::SupportsContentType aContType: %S", &aContType);
+	TPtr bufPtr(iContentTypes->Des());
+
+	if( bufPtr.Length() == 0 )
+		{
+		LOG("CBtDiscover::SupportsContentType length == 0");
+		return EFalse;
+		}
+
+	if( bufPtr.Find(aContType) == KErrNotFound )
+		{
+		LOG("CBtDiscover::SupportsContentType Find == KErrNotFound");
+		return EFalse;
+		}
+	else
+		{
+		LOG("CBtDiscover::SupportsContentType Found");
+		return ETrue;
+		}
+	}
+
+/**
+  * @brief Called from TBTAttribValVisitor to set the printer model.
+  * @param aString String of semi-colon separated printer details.
+  */
+void CBtDiscover::SetPrinterModelL(const TPtrC8 &aString)
+	{
+	LOG81("CBtDiscover::SetPrinterModelL aString: %S", &aString);
+
+	if(iPrinterModel)
+		delete iPrinterModel;
+	iPrinterModel = NULL;
+	
+	_LIT8(KModelString, ";MDL:");
+	TInt pos = aString.Find( KModelString );
+	LOG1("[CBtDiscover::SetPrinterModelL]\t pos: %d", pos);
+	if( pos != KErrNotFound )
+		{
+		TPtrC8 tmpStr = aString.Right(aString.Length() - pos - 5);
+		pos = tmpStr.Locate(';');
+		if( pos != KErrNotFound )
+			{
+			iPrinterModel = HBufC::NewL(pos);
+			iPrinterModel->Des().Copy(tmpStr.Left(pos));
+			}
+		}
+
+	LOG("CBtDiscover::SetPrinterModelL end");
+	}
+
+/**
+ * @brief Obtain Printer Model.
+ *
+ * Gets a TDesC& to the printer model previously set with SetPrinterModelL(),
+ * or KNullDesC if no printer model has been set.
+ *
+ * @return String with the printer model.
+ */
+const TDesC& CBtDiscover::GetPrinterModel() const
+	{
+	if (iPrinterModel)
+		return *iPrinterModel;
+	else
+		return KNullDesC;
+	}
+
+void CBtDiscover::SetVendor( TPrinter::TPrinterVendor aVendor )
+	{
+	LOG1("CBtDiscover::SetVendor aVendor: %d", aVendor);
+	iVendor = aVendor;
+	}
+
+TPrinter::TPrinterVendor CBtDiscover::GetVendor() const
+	{
+	LOG1("CBtDiscover::GetVendor return: %d", iVendor);
+	return iVendor;
+	}
+
+void CBtDiscover::GetNextAttributeL(TSdpServRecordHandle aHandle)
+	{
+	LOG1("[CBtDiscover::GetNextAttributeL]\t iCurrentSDPAttrID: 0x%X", iCurrentSDPAttrID);
+
+	TSdpAttributeID nextId;
+
+	switch( iCurrentSDPAttrID )
+	{
+		case KSdpAttrIdServiceClassIDList:
+			nextId = KSdpAttrIdProtocolDescriptorList;
+			break;
+		case KSdpAttrIdProtocolDescriptorList:
+			nextId = KSdpAttrIdAdditionalProtocolDescriptorLists;
+			break;
+		case KSdpAttrIdAdditionalProtocolDescriptorLists:
+			nextId = KSdpAttrIdDocFormatsSupported;
+			break;
+		case KSdpAttrIdDocFormatsSupported:
+			nextId = KSdpAttrIdModelID;
+			break;
+		case KSdpAttrIdModelID:
+			nextId = KSdpAttrIdMaxWidth;
+			break;
+		case KSdpAttrIdMaxWidth:
+			nextId = KSdpAttrIdMaxLen;
+			break;
+		default:
+			// all interested attributes received, at tleast asked
+			//Request 0xFFFFFF to indicate all done
+			nextId = KSdpAttrIdAllDone;
+			break;
+	}
+
+	iAgent->AttributeRequestL( aHandle, nextId );
+
+	LOG1("[CBtDiscover::GetNextAttributeL]\t end. nextId: 0x%X", nextId);
+	}
+
+
+
+/************************************************************************/
+/* visitor */
+/************************************************************************/
+
+CBTAttribValVisitor* CBTAttribValVisitor::NewLC( CBtDiscover& aDiscover )
+	{
+	CBTAttribValVisitor* obj = new (ELeave) CBTAttribValVisitor( aDiscover );
+	CleanupStack::PushL( obj );
+	return obj;
+	}
+
+/**
+  * @param aDiscover Stores a pointer to the CBtDiscover object, in order to call
+  * CBtDiscover::SetPort and CBtDiscover::SetContentTypes once those attributes have been
+  * found.
+  */
+CBTAttribValVisitor::CBTAttribValVisitor(CBtDiscover& aDiscover) : iDiscover(aDiscover)
+	{
+	}
+
+/**
+  * @brief Called for each of the individual attributes in the SDP record being parsed.
+  * @param aValue Value of the attribute.
+  * @param aType Type of the attribute.
+  */
+void CBTAttribValVisitor::VisitAttributeValueL(CSdpAttrValue& aValue, TSdpElementType aType)
+	{
+	LOG1("[CBTAttribValVisitor::VisitAttributeValueL]\t Begin. aType: %d", aType);
+
+	switch ( aType )
+		{
+		case ETypeUUID:
+			CheckUuid(aValue.UUID());
+			break;
+		case ETypeUint:
+			CheckUint(aValue.Uint());
+			break;
+		case ETypeString:
+			switch (iDiscover.iCurrentSDPAttrID)
+				{
+				case KSdpAttrIdDocFormatsSupported:
+					iDiscover.SetContentTypesL(aValue.Des());
+					break;
+				case KSdpAttrIdModelID:
+					CheckVendor( aValue.Des() );
+					iDiscover.SetPrinterModelL(aValue.Des());
+					break;
+				default:
+					break;
+				}
+			break;
+		default:
+			break;
+		}
+	}
+
+/**
+  * @brief Indicates the beginning of an attribute list in the SDP record.
+  */
+void CBTAttribValVisitor::StartListL(CSdpAttrValueList& /*aList*/)
+	{
+	LOG("CBTAttribValVisitor::StartListL");
+	}
+
+/**
+  * @brief Indicates the end of an attribute list in the SDP record.
+  */
+void CBTAttribValVisitor::EndListL()
+	{
+	LOG("CBTAttribValVisitor::EndListL");
+	}
+
+CBTAttribValVisitor::~CBTAttribValVisitor()
+	{
+	}
+
+//--------------------------------------------------------------------------------------------
+//
+// CBtPrinterController::CheckUuid
+//
+//--------------------------------------------------------------------------------------------
+void CBTAttribValVisitor::CheckUuid( const TUUID& aUuid )
+{
+	LOG("[CBTAttribValVisitor::CheckUuid]\t begin");
+
+	// We're interested only about UUIDs received at KSdpAttrIdServiceClassIDList
+	if (KSdpAttrIdServiceClassIDList != iDiscover.iCurrentSDPAttrID)
+	{
+		LOG1("[CBTAttribValVisitor::CheckUuid]\t Wrong attribute ID: 0x%X",iDiscover.iCurrentSDPAttrID);
+		return;
+	}
+
+#ifdef _DEBUG
+	TBuf<20> uidstr;
+	_LIT(KUidForm, "0x%02X%02X%02X%02X");
+	uidstr.Format(KUidForm, aUuid[0], aUuid[1], aUuid[2], aUuid[3]);
+	LOG1("[CBTAttribValVisitor::CheckUuid]\t ETypeUUID: %S", &uidstr);
+#endif
+
+	TUUID uidOpp(KBTSDPOBEXObjectPush);
+	TUUID uidBpp(KBTSDPDirectPrinting);
+	TUUID uidSts(KBTSDPPrintingStatus);
+	
+	if(aUuid == uidOpp)
+		iDiscover.iCurrentServiceUUID = KBTSDPOBEXObjectPush;
+	if(aUuid == uidBpp)
+		iDiscover.iCurrentServiceUUID = KBTSDPDirectPrinting;
+	if(aUuid == uidSts)
+		iDiscover.iFoundSTS = ETrue;
+
+	LOG("[CBTAttribValVisitor::CheckUuid]\t end");
+}
+
+//--------------------------------------------------------------------------------------------
+//
+// CBtPrinterController::CheckUint
+//
+//--------------------------------------------------------------------------------------------
+void CBTAttribValVisitor::CheckUint( const TUint& aValue )
+{
+	LOG("[CBTAttribValVisitor::CheckUint]\t begin");
+	
+	switch(iDiscover.iCurrentSDPAttrID)
+	{
+		case KSdpAttrIdProtocolDescriptorList:
+			if (KBTSDPDirectPrinting == iDiscover.iCurrentServiceUUID)
+			{
+				LOG1("[CBTAttribValVisitor::CheckUint]\t  iDiscover->SetBPPPort(%d)", aValue);
+				iDiscover.SetBPPPort(aValue);
+			}
+			if (KBTSDPOBEXObjectPush == iDiscover.iCurrentServiceUUID)
+			{
+				LOG1("[CBTAttribValVisitor::CheckUint]\t  iDiscover->SetOPPPort(%d)", aValue);
+				iDiscover.SetOPPPort(aValue);
+			}
+			break;
+		case KSdpAttrIdAdditionalProtocolDescriptorLists:
+			if(iDiscover.iFoundSTS )
+			{
+				LOG1("[CBTAttribValVisitor::CheckUint]\t iDiscover->SetSTSPort(%d)", aValue);
+				iDiscover.SetSTSPort(aValue);
+				iDiscover.iFoundSTS = EFalse;
+			}
+			break;
+		case KSdpAttrIdMaxWidth:
+			LOG1("[CBTAttribValVisitor::CheckUint]\t KSdpAttrIdMaxWidth: %d", aValue);
+			break;
+		case KSdpAttrIdMaxLen:
+			LOG1("[CBTAttribValVisitor::CheckUint]\t KSdpAttrIdMaxLen: %d", aValue);
+			break;
+		default:
+			break;
+	}
+
+	LOG("[CBTAttribValVisitor::CheckUint]\t end");
+}
+
+//--------------------------------------------------------------------------------------------
+//
+// CBtPrinterController::CheckVendor
+//
+//--------------------------------------------------------------------------------------------
+void CBTAttribValVisitor::CheckVendor( const TDesC8&  aVendor  )
+	{
+	LOG("[CBTAttribValVisitor::CheckVendor]\t Begin");
+	iDiscover.SetVendor( TPrinter::EVendorNone );
+	// At the moment this functionality is not used, but no Vendor is set.
+	HBufC8* vendorName = aVendor.Alloc();
+	if( vendorName )
+	{
+		LOG81("[CBTAttribValVisitor::CheckVendor]\t vendorName: %S", vendorName);
+		TPtr8 ptr = vendorName->Des();
+		ptr.LowerCase();
+		_LIT8( KHp, "mfg:hp;" );
+
+		TInt res = vendorName->Find( KHp );
+		LOG1("[CBTAttribValVisitor::CheckVendor]\t res: %d", res);
+		
+		if( res != KErrNotFound ) 
+			iDiscover.SetVendor( TPrinter::EVendorHp );
+	}
+	delete vendorName;
+	LOG("[CBTAttribValVisitor::CheckVendor]\t End");
+	}
+
+//  End of File