// 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:
// BT Host Resolver test code
// 
//

#include "teir.h"
#include <utf.h>
#include <btsdp.h>
#include <e32math.h>

#define USE_SMALL_CONSOLE 0

typedef TBuf<20> TBTDevAddrBuf;
const TUint8 KEIRDataOffset = 1;
//_LIT(KManufacturerData, "This is a very very very very very very very very very very very very very very very very very very very very very very very very very very long data from phone manufacturer!");
_LIT(KManufacturerData, "This is a very very long data from phone manufacturer!");
_LIT(KManufacturerData1, "");


GLDEF_D RTest test(_L("Extended Inquiry Response tests"));

GLDEF_D TBTDevAddr gDevAddr(MAKE_TINT64(0x0002, 0x5b019a39)); // csr
//GLDEF_D TBTDevAddr gDevAddr(MAKE_TINT64(0x001b, 0xdc0f5cae)); // EIR windows
GLDEF_D TSize gSmallConsole(75,50);

//TInt CActiveHostResolver::iHRNumber = 0;



/** 
 Appends a TDesC string to a TDes string. 
 
 @param aDes String, which aOrg appends to
 @param aDesC String to be appended to aDest
 @return EFalse, if there not enough space in the TDes sting to contain the TDesC string. Otherwise, ETrue.
 */
TBool AppendDes(TDes& aDest, const TDesC& aOrg)
	{
	if((aDest.MaxLength() - aDest.Length()) < aOrg.Length())
		return EFalse;

	aDest.Append(aOrg);

	return ETrue;
	}


// ----------------------------------------------------------------------------
//							EIR App
// ----------------------------------------------------------------------------

CEirApp::CEirApp(CConsoleBase& aConsole)
: iConsole(aConsole)
	{
	}

CEirApp::~CEirApp()
	{
	iResolvers.ResetAndDestroy();
	iResolvers.Close();

	iDevicesFound.Close();

	StopProtocols();

	delete iActiveConsole;
	delete iNameRequester;

	if(iEirPublisherTestManuSpecific)
		{
		delete iManuNotifier;
		delete iEirPublisherTestManuSpecific;
		iManuNotifier = NULL;
		iEirPublisherTestManuSpecific = NULL;
		}
	iSdpDB.Close();
	}

CEirApp* CEirApp::NewL(CConsoleBase& aConsole)
	{
	CEirApp* thisapp = new (ELeave) CEirApp(aConsole);
	CleanupStack::PushL(thisapp);
	thisapp->ConstructL(aConsole);
	CleanupStack::Pop(thisapp);
	return thisapp;
	}

void CEirApp::ConstructL(CConsoleBase& aConsole)
	{
	iConsole.Printf(_L("Using HCI_V2\n"));
	iConsole.Printf(_L("Bluetooth Protocols Loading...\n"));
    InitialiseL();
    iConsole.Printf(_L("Bluetooth Protocols Loaded.\n"));
	ShowAddress();
	iActiveConsole = CActiveConsole::NewL(aConsole, *this);
	iManuNotifier = CManufacturerDataNotifier::NewL(*this);
	iEirPublisherTestManuSpecific = CEirPublisherManufacturerSpecificData::NewL(*iManuNotifier);
	}

void CEirApp::StartL()
	{
	// go ....
	CActiveScheduler::Start();
	}

void CEirApp::Stop()
	{
	CActiveScheduler::Stop();
	}
	
void CEirApp::SetPublishManuData(TUint aAvailableDataSize)
	{
	// TODO: some logic can be added here to cope with the given aAvailableDataSize
	// maybe truncating the intended data 
	HBufC8* buf = HBufC8::NewL(240);// LocalName-granular truncation
	buf->Des().Append(KManufacturerData);
	if(buf->Des().Length() > aAvailableDataSize)
		{
		TPtrC8 ptr(buf->Left(aAvailableDataSize));
		iEirPublisherTestManuSpecific->SetManufacturerSpecificData(ptr);
		}
	else
		{
		iEirPublisherTestManuSpecific->SetManufacturerSpecificData(*buf);
		}
	delete buf;
	}

void CEirApp::NewEirPublisherTestL(TTestEirTag aTag)
	{
	switch(aTag)
		{
	case ETestDeviceName:
		iResolvers.Append(CActiveInquirer::NewL(iConsole, *this, 0, EFalse, EFalse, EFalse, EFalse));
		iNameRequester->SetLocalNameL();
		break;

	case ETestUuid16:
	
		Register16BitSDPRecordL();
		break;
	
	case ETestUuid128:
		Register128BitSDPRecordL();
		break;
	
	case ETestSetMSD:		
		if(!iManuNotifier && !iEirPublisherTestManuSpecific)
			{
			iManuNotifier = CManufacturerDataNotifier::NewL(*this);
			iEirPublisherTestManuSpecific = CEirPublisherManufacturerSpecificData::NewL(*iManuNotifier);
			}
		iEirPublisherTestManuSpecific->RequestPublishDataSize(55);
		break;
	case ETestRemoveMSD:
		if(iEirPublisherTestManuSpecific)
			{
			delete iEirPublisherTestManuSpecific;
			}
		break;
	default:
        iConsole.Printf(_L("INVALID EIR TAG!\n"));
		//User::Panic();
		break;	
		}
	}

void CEirApp::Register16BitSDPRecordL()
	{
	CreateSdpRecordL(0x1116);
	CreateSdpRecordL(0x1234, EFalse);
	CreateSdpRecordL(0xAABB);
	CreateSdpRecordL(0xF00D);
	CreateSdpRecordL(0x0E0D);
	}

void CEirApp::Register128BitSDPRecordL()
	{
	// Set Attr 1 (service class list) to list with UUID = 0x0123456789abcdef
	TUUID uuid1280 = TUUID(0x00000000, 0x00000000, 0x00000000, 0x00000000);
	TUUID uuid1281 = TUUID(0x11111111, 0x11111111, 0x11111111, 0x11111111);
	TUUID uuid1282 = TUUID(0x22222222, 0x22222222, 0x22222222, 0x22222222);
	TUUID uuid1283 = TUUID(0x33333333, 0x33333333, 0x33333333, 0x33333333);
	TUUID uuid1284 = TUUID(0x44444444, 0x44444444, 0x44444444, 0x44444444);
	TUUID uuid1285 = TUUID(0x55555555, 0x55555555, 0x55555555, 0x55555555);
	TUUID uuid1286 = TUUID(0x66666666, 0x66666666, 0x66666666, 0x66666666);
	TUUID uuid1287 = TUUID(0x77777777, 0x77777777, 0x77777777, 0x77777777);
	TUUID uuid1288 = TUUID(0x88888888, 0x88888888, 0x88888888, 0x88888888);
	TUUID uuid1289 = TUUID(0x99999999, 0x99999999, 0x99999999, 0x99999999);
	
	CreateSdpRecordL(uuid1280);
	CreateSdpRecordL(uuid1281, EFalse, EFalse);
	CreateSdpRecordL(uuid1282);
	CreateSdpRecordL(uuid1283);
	CreateSdpRecordL(uuid1284);
	CreateSdpRecordL(uuid1285);
	CreateSdpRecordL(uuid1286);
	CreateSdpRecordL(uuid1287);
	CreateSdpRecordL(uuid1288);
	CreateSdpRecordL(uuid1289);
	}

void CEirApp::CreateSdpRecordL(const TUUID& aUUID,
							  TBool aIsPublicBrowseGroup,
							  TBool aIsL2cap)
	{
	TSdpServRecordHandle recHandle;
	CSdpAttrValueDES* attrValDES = NULL;

	iSdpDB.CreateServiceRecordL(aUUID, recHandle);
	if(aIsPublicBrowseGroup)
		{
		// Browse Group List
		attrValDES = CSdpAttrValueDES::NewDESL(NULL);
		CleanupStack::PushL(attrValDES);
		attrValDES
			->StartListL()
			->BuildUUIDL(TUUID(TUint16(KPublicBrowseGroupUUID))) // Public Browse Group
			->EndListL();
		iSdpDB.UpdateAttributeL(recHandle, KSdpAttrIdBrowseGroupList, *attrValDES);
		CleanupStack::PopAndDestroy(attrValDES);
		}
	
	if(aIsL2cap)
		{
		// protocol Descriptor List
		attrValDES = CSdpAttrValueDES::NewDESL(NULL);
		CleanupStack::PushL(attrValDES);		
		attrValDES
			->StartListL()
			->BuildUUIDL(TUUID(KL2CAPUUID))
			->EndListL();
		iSdpDB.UpdateAttributeL(recHandle, KSdpAttrIdProtocolDescriptorList, *attrValDES);
		CleanupStack::PopAndDestroy(attrValDES);
		}
	}

void CEirApp::NewNameRequesterL(TBool aEirInstead, TBool aIgnoreCache)
	{
	iResolvers.Append(CActiveNameRequester::NewL(iConsole, *this, aEirInstead, aIgnoreCache));
	}

void CEirApp::NewInquirerL(TUint aIAC, TBool aDoNames, TBool aIgnoreCache, TBool aDoEir, TBool forever)
	{
	iResolvers.Append(CActiveInquirer::NewL(iConsole, *this, aIAC, aDoNames, aIgnoreCache, aDoEir, forever));
	}

void CEirApp::CancelInquirer()
	{
	TInt numresolvers = iResolvers.Count();
	if (numresolvers)
		iResolvers[numresolvers-1]->Cancel(); //FIXME! Cancel's the newest host resolver
	else
		iConsole.Printf(_L("No resolver to cancel!\n"));
	}

void CEirApp::NewNameArrayRequesterL()
	{
	iResolvers.Append(CActiveNameArrayRequester::NewL(iConsole, *this, iDevicesFound));
	}

void CEirApp::DeviceFound(TBTDevAddr aAddress)
	{
#if USE_SMALL_CONSOLE
	iConsole.Printf(_L("App: Found device.\n"));
#endif
	iDevicesFound.Append(aAddress);
	}

void CEirApp::InquiryComplete()
	{
	iConsole.Printf(_L("App: No more devices.\n"));
	iActiveConsole->DrawCursor();
	}

RSocketServ& CEirApp::SocketServ() 
	{
	return iSocketServer;
	}

void CEirApp::InitialiseL()
	{
	_LIT(KLDDName, "ECOMM");

#if defined (__WINS__)
	iConsole.Printf(_L("Initialise using EMULATOR PDD\n"));

	_LIT(KPDDName, "ECDRV");
#else
	iConsole.Printf(_L("Initialise using H2 PDD\n"));

	_LIT(KPDDName, "EUART1");
#endif

	TInt rerr = KErrNone;

	rerr = User::LoadPhysicalDevice(KPDDName);
	if(rerr != KErrNone && rerr != KErrAlreadyExists)
		{
		iConsole.Printf(_L("Failed Load Physical Device %d!\n") ,rerr);
		User::Leave(rerr);
		}
	else 
		{
		iConsole.Printf(_L("Loaded LDD\n"));
		}

	rerr = User::LoadLogicalDevice(KLDDName);
	if(rerr != KErrNone && rerr != KErrAlreadyExists)
	{
		iConsole.Printf(_L("Failed Load Logical Device%d!\n") ,rerr);
		User::Leave(rerr);
		}
	else 
		{
		iConsole.Printf(_L("Loaded PDD\n"));
		}
	User::LeaveIfError(iSocketServer.Connect());

	rerr = iSdp.Connect();
	if(rerr == KErrNone)
		{
		rerr = iSdpDB.Open(iSdp);
		}

	TProtocolDesc iProtocolInfo;
	TRequestStatus status;

	User::LeaveIfError(iSocketServer.FindProtocol(_L("L2CAP"), iProtocolInfo));
	iSocketServer.StartProtocol(iProtocolInfo.iAddrFamily, iProtocolInfo.iSockType, iProtocolInfo.iProtocol, status);
	User::WaitForRequest(status);

	User::LeaveIfError(iSocketServer.FindProtocol(_L("RFCOMM"), iProtocolInfo));

	User::LeaveIfError(iSocketServer.FindProtocol(_L("BTLinkManager"), iProtocolInfo));
	}

void CEirApp::StopProtocols()
	{
	TInt err;
	TProtocolDesc iProtocolInfo;
	TRequestStatus status;

	if(!iSocketServer.Handle())
		{
		// if the socket server has not even been opened
		// then there is no point trying to unload protocols
		return;
		}

	err = iSocketServer.FindProtocol(_L("L2CAP"), iProtocolInfo);
	if(err)
		{
		iConsole.Printf(_L("Failed to find L2CAP protocol. ERROR %d\n"), err);
		}

	iSocketServer.StopProtocol(iProtocolInfo.iAddrFamily, iProtocolInfo.iSockType, iProtocolInfo.iProtocol, status);
	User::WaitForRequest(status);

	err = iSocketServer.FindProtocol(_L("RFCOMM"), iProtocolInfo);
	if(err)
		{
		iConsole.Printf(_L("Failed to find RFCOMM protocol. ERROR %d\n"), err);
		}

	iSocketServer.StopProtocol(iProtocolInfo.iAddrFamily, iProtocolInfo.iSockType, iProtocolInfo.iProtocol, status);
	User::WaitForRequest(status);
	}

void CEirApp::ShowAddress()
/**
	Shows the example of using a synchronous Get to retrieve local BTAddr

*/
	{
	RProperty property;
	TBuf8<6> addr;
	TRequestStatus status;

	iConsole.Printf(_L("Waiting for Local BDADDR...\n"));
	// Wait for the address to be set
	TInt err = property.Attach(KPropertyUidBluetoothCategory, KPropertyKeyBluetoothGetLocalDeviceAddress);
	
	if(err)
		{
		iConsole.Printf(_L("P&S: ERROR attaching to local address\n"));
		return;
		}

	RTimer addrTimer;
	err = addrTimer.CreateLocal();
	if(err != KErrNone)
		{
		iConsole.Printf(_L("Error creating P&S timer (%d)\n"), err);
		property.Close();
		return;
		}
	TRequestStatus timerStatus;
	
	property.Subscribe(status);
	addrTimer.After(timerStatus, 5000000); // 5 seconds.
	User::WaitForRequest(status, timerStatus);
	
	addrTimer.Close(); // done with timer now.

	err = property.Get(addr);

	if (err)
		{	
		iConsole.Printf(_L("P&S: ERROR getting local address\n"));
		}
	else
		{
		iConsole.Printf(_L("Local Address: %02X:%02X:%02X:%02X:%02X:%02X\n"), addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
		}
	
	// Set inquiry scan
	RProperty discprop;
	err=discprop.Set(KPropertyUidBluetoothCategory, KPropertyKeyBluetoothSetScanningStatus, EInquiryAndPageScan);
	if (err)
		{	
		iConsole.Printf(_L("P&S: ERROR setting discoverability\n"));
		}
	else
		{
		iConsole.Printf(_L("Device now in discoverable mode\n"));
		}
	property.Close();

	User::WaitForRequest(status, timerStatus);
	}





// ----------------------------------------------------------------------------
//							Active Host Resolver
// ----------------------------------------------------------------------------

CActiveHostResolver::CActiveHostResolver(CConsoleBase& aConsole, CEirApp& aApp)
: CActive(EPriorityStandard), iConsole(aConsole), iApp(aApp)
	{
	//++iHRNumber;
	CActiveScheduler::Add(this);
	}

CActiveHostResolver::~CActiveHostResolver()
	{
//	if (iHostResolver.SubSessionHandle())
//		iHostResolver.Cancel();
	Cancel();
	iHostResolver.Close();
#if USE_SMALL_CONSOLE
	delete iHROutputConsole;
#endif
	}

void CActiveHostResolver::StartServerL()
/** 
  Connect to socket server and get BTLinkManager protocol info
 **/
	{
	iSocketServer = iApp.SocketServ();
	}

void CActiveHostResolver::StartHostResolverL()
/** 
	In future have a bunch of host resolvers
*/
	{
	iConsole.Printf(_L("Opening Host Resolver ..\n"));

        User::LeaveIfError(iSocketServer.FindProtocol(_L("BTLinkManager"), iProtocolInfo));
	User::LeaveIfError(iHostResolver.Open(iSocketServer,
											iProtocolInfo.iAddrFamily,
											iProtocolInfo.iProtocol));
//#if 0
	// show Localname for free
	TBTDeviceName localName;
	iHostResolver.GetHostName(localName);
	iConsole.Printf(_L("Local hostname is %S\n"), &localName);					
//#endif
	}

TInt CActiveHostResolver::RunError(TInt /*aError*/)
	{
	__DEBUGGER();
	return KErrNone;
	}

void CActiveHostResolver::SetLocalNameL()
	{
	iConsole.Printf(_L("Enter local device name (esc to finish)"));

	const TUint8 KMaxLength = 16;
	TBuf<KMaxLength> localName;
	localName.Zero();
	FOREVER
		{
		TChar c = test.Getch();
		if (c == EKeyEscape)
			break;
		else
			{
			TBuf<1> buf;
			buf.Append(c);
			iConsole.Printf(buf);
			localName.Append(c);
			}

		if(localName.Length() >= KMaxLength)
			// User entered too many characters...
			{
			// ...get them to start again
			iConsole.Printf(_L("\nToo many chars!\n"));
			iConsole.Printf(_L("(max %d chars) <ESC> to finish: "), KMaxLength);
			localName.Zero();
			}
		} // END - FOREVER loop

	// If the name is OK, send it to the hardware... 
	if (localName.Length()>0)
		{
		User::LeaveIfError(iHostResolver.SetHostName(localName));
		}
	else
		{
		User::LeaveIfError(KErrGeneral);
		}
	}

void CActiveHostResolver::SetIAC(TUint aIAC)
	{
	iInquiryRequest.SetIAC(aIAC);
	}


void CActiveHostResolver::DoCancel()
	{
	iHROutputConsole->Printf(_L("Cancelled.\n"));
	iHostResolver.Cancel();
	}

void CActiveHostResolver::ConstructL()
	{
#if USE_SMALL_CONSOLE
	iHROutputConsole = Console::NewL(_L("Host Resolver output"),gSmallConsole);
#else
	iHROutputConsole = &iConsole;
#endif

	StartServerL();
	StartHostResolverL();
	}

// ----------------------------------------------------------------------------
//							Active Inquirer
// ----------------------------------------------------------------------------

CActiveInquirer::CActiveInquirer(CConsoleBase& aConsole, CEirApp& aApp, TBool aDoNames, TBool aIgnoreCache, TBool aDoEir, TBool forever)
: CActiveHostResolver(aConsole, aApp), iDoNames(aDoNames), iIgnoreCache(aIgnoreCache), iDoEir(aDoEir), iForever(forever)
	{
	}

CActiveInquirer* CActiveInquirer::NewL(CConsoleBase& aConsole, CEirApp& aApp, TUint aIAC, TBool aDoNames, TBool aIgnoreCache, TBool aDoEir, TBool forever)
	{
	CActiveInquirer* inquirer = new (ELeave) CActiveInquirer(aConsole, aApp, aDoNames, aIgnoreCache, aDoEir, forever);
	CleanupStack::PushL(inquirer);
	inquirer->ConstructL(aIAC);
	CleanupStack::Pop(inquirer);
	return inquirer;
	}

void CActiveInquirer::ConstructL(TUint aIAC)
	{
	CActiveHostResolver::ConstructL();

//	SetLocalNameL();
	TUint action = KHostResInquiry;

	if (iDoNames)
		action |= KHostResName;

	if (iIgnoreCache)
		action |= KHostResIgnoreCache;

	if (iDoEir)
		action |= KHostResEir;

	iHROutputConsole->Printf(_L("Action: 0x%x\n"), action); 
	iInquiryRequest.SetAction(action);
	iInquiryRequest.SetIAC(aIAC);

	Start();
	}

void CActiveInquirer::Start()
/**
	Start an Inquiry
**/
	{
	TUint currentIAC;
	currentIAC = iInquiryRequest.IAC();

	iHROutputConsole->Printf(_L("Inquiring [IAC 0x%06x]\n"), currentIAC);

	iHostResolver.GetByAddress(iInquiryRequest, iResult, iStatus); // async
	SetActive();
	}

void CActiveInquirer::RunL()
	{
	TReal passLoss = 0;
	TReal estimatedDistance = 0;
	if (iStatus.Int() == KErrNone)
		{
		TInquirySockAddr& sa = TInquirySockAddr::Cast(iResult().iAddr);
		const TBTDevAddr& bdaddr = sa.BTAddr();
		
		iApp.DeviceFound(bdaddr); // notify app

		TBTDevAddrBuf bdaddrbuf;
		bdaddr.GetReadable(bdaddrbuf);

		iHROutputConsole->Printf(_L("Result: BdAddr = 0x%S"),&bdaddrbuf);

		if(sa.ResultFlags() & TInquirySockAddr::ERssiValid) 
			{
			iHROutputConsole->Printf(_L(" RSSI: %d ["), sa.Rssi());
			// store the rssi value in here
			passLoss = sa.Rssi();
#define KRssiBarNum 8
#define KRssiRange (127 + 20 + 1) 
			TUint rssiBars = ((((TInt) sa.Rssi()) + 127 + 1) * KRssiBarNum) / KRssiRange; 
			TUint i;
		
			for(i=1; i <= KRssiBarNum ; i++)
				{
				if(rssiBars >= i)
					{
					iHROutputConsole->Printf(_L("*")); 
					}
				else
					{
					iHROutputConsole->Printf(_L(" ")); 
					}
				}
			iHROutputConsole->Printf(_L("]\n")); 
			}
		else
			{
			iHROutputConsole->Printf(_L("\n")); 
			}

		if(iDoEir)
			{
			TBluetoothNameRecordWrapper eir(iResult());
			//iHROutputConsole->Printf(_L("EIR Length: %d\n"), eir.Length()); 
			TBool isNameComplete;
			TInt error = KErrNone;
			TInt length = 0;
			
			
			// Go and get name
			// This length could be used to create the TBuf to be passed into GetDeviceName()
			length = eir.GetDeviceNameLength();
			TBuf<255> name;
			if(length >= 0)
				{
				// name will contain a Unicode encoded 16-bit string
				error = eir.GetDeviceName(name, isNameComplete);
				}
			else
				{
				error = length;
				}
			if(error == KErrNone)
				// Now we have name here
				{
				if(isNameComplete == EFalse)
					{
					iHROutputConsole->Printf(_L("%d Bytes [Partial] Name: "), length);
					}
				else
					{
					iHROutputConsole->Printf(_L("%d Bytes [Complete] Name: "), length);
					}
				iHROutputConsole->Printf(_L("%S \n"),&name);
				}
			
			// Get TxPowerLevel
			TInt8 txPowerLevel;
			error = eir.GetTxPowerLevel(txPowerLevel);
			if(error == KErrNone)
				// TxPowerLevel present
				{
				iHROutputConsole->Printf(_L("TxPowerLevel: %ddBm\n"), txPowerLevel);
				// NICE one, we can calculate the pass loss here then :)
				if(passLoss != 0)
					{
					passLoss = txPowerLevel - passLoss;
					iHROutputConsole->Printf(_L("Pass Loss: %fdBm\n"), passLoss);
					// Here comes the magic equation:
					TReal power = (passLoss / 20.0) - 2.00221;
					TReal src = 10.0;
					Math::Pow(estimatedDistance, src, power);
					iHROutputConsole->Printf(_L("Estimated distance: %fm\n"), estimatedDistance);
					}
				}
			// Get UUIDs
			RExtendedInquiryResponseUUIDContainer uuidContainer;
			error = eir.GetServiceClassUuids(uuidContainer);
			if(error >= KErrNone)
				{
				RArray<TUUID> uuids;
				TInt uuidCount = uuidContainer.UUIDs().Count();
				if(uuidCount > 0)
					{
					iHROutputConsole->Printf(_L("*** UUID Count: %d\n"), uuidCount);
					TInt i;
					for(i=0;i<uuidCount;i++)
						{
						//iHROutputConsole->Printf(_L("*** UUID #%d minsz: %d value: 0x"), i, uuidContainer.UUIDs()[i].MinimumSize());
						TInt j;
						TPtrC8 uuid(uuidContainer.UUIDs()[i].ShortestForm());
						// Treat it as a big endian
						for(j=0;j<uuid.Length();j++)
							{
							iHROutputConsole->Printf(_L("%02X"), uuid[j]);
							}
						
						iHROutputConsole->Printf(_L(" \n"));
						}
					}
				}
			
			// Get Manufacturer Specific Data
			length = eir.GetVendorSpecificDataLength();
			TBuf8<255> msd;
			if(length > 0)
				{
				error = eir.GetVendorSpecificData(msd);
				}
			else
				{
				error = length;
				}
			if(error == KErrNone)
				// we have Manufacturer Specific Data here
				{
				// This conversion is for display reason, in a real world this may not be necessary as 
				// Manufacturer specific data can be just raw 8-bit data, however GetDeviceName() is
				// different as it always return Unicode encoded 16-bit string
				error = CnvUtfConverter::ConvertToUnicodeFromUtf8(name, msd);
				if(error >= KErrNone && length > 0)
					{
					iHROutputConsole->Printf(_L("%d Bytes Manufacturer Specific Data: %S\n"), length, &name);
					}
				}
			}

		if (iDoNames)
			{
			if(!iDoEir)
				{
				TBuf<255> namebuf;
				namebuf.Copy(iResult().iName);
				iHROutputConsole->Printf(_L("Remote name \"%S\""), &namebuf);
				if(iResult().iFlags & TNameRecord::EPartial)
						{
						iHROutputConsole->Printf(_L(" [PARTIAL]\n"));
						}
					else
						{
						iHROutputConsole->Printf(_L(" [COMPLETE]\n"));
						}
				}
			else
				{
				TBluetoothNameRecordWrapper eir(iResult());
				TInt length = eir.GetDeviceNameLength();
				TInt error = KErrNone;
				TBuf<255> name;
				TBool isNameComplete;

				if(length >= 0)
					{
					// name will contain a Unicode encoded 16-bit string
					error = eir.GetDeviceName(name, isNameComplete);
					}
				else
					{
					error = length;
					}
				if(error == KErrNone)
					// Now we have name here
					{
					if(isNameComplete == EFalse)
						{
						iHROutputConsole->Printf(_L("%d Bytes [Partial] Name (via EIR wrapper): "), length);
						}
					else
						{
						iHROutputConsole->Printf(_L("%d Bytes [Complete] Name (via EIR wrapper): "), length);
						}
					iHROutputConsole->Printf(_L("%S \n"),&name);
					}
				}
			}

		iHostResolver.Next(iResult, iStatus); // asynch
		SetActive();
		}
	else if (iStatus.Int() == KErrEof)
		{
		iHROutputConsole->Printf(_L("Finished inquiry.\n"));

		if(iForever)
			{
			iHROutputConsole->Printf(_L("Restarting inquiry.\n"));
			Start();
			}
		else
			{
			iApp.InquiryComplete();
			}
		}
	else
		{
		iHROutputConsole->Printf(_L("Inquiry request failed with result %d.\n"), iStatus.Int());
		}
	}


// ----------------------------------------------------------------------------
//							Active Name Requester
// ----------------------------------------------------------------------------

void CActiveNameRequester::ConstructL()
	{
	CActiveHostResolver::ConstructL();

	TUint action = iEirInstead ? KHostResEir : KHostResName;
	if (iIgnoreCache)
		action |= KHostResIgnoreCache;
	iInquiryRequest.SetAction(action);
	Start();
	}

CActiveNameRequester::CActiveNameRequester(CConsoleBase& aConsole, CEirApp& aApp, TBool aEirInstead, TBool aIgnoreCache)
: CActiveHostResolver(aConsole, aApp), iEirInstead(aEirInstead), iIgnoreCache(aIgnoreCache)
	{
	}

CActiveNameRequester* CActiveNameRequester::NewL(CConsoleBase& aConsole, CEirApp& aApp, TBool aEirInstead, TBool aIgnoreCache)
	{
	CActiveNameRequester* namerequester = new (ELeave) CActiveNameRequester(aConsole, aApp, aEirInstead, aIgnoreCache);
	CleanupStack::PushL(namerequester);
	namerequester->ConstructL();
	CleanupStack::Pop();
	return namerequester;
	}


void CActiveNameRequester::Start()
/**
	Get just the name
**/
	{
	iInquiryRequest.SetBTAddr(gDevAddr); // this object does the global addr for the moment

	TBTDevAddrBuf bdaddrbuf;
	gDevAddr.GetReadable(bdaddrbuf);

	iHROutputConsole->Printf(_L("Looking up address %S...\r\n"),&bdaddrbuf);

	iHostResolver.GetByAddress(iInquiryRequest, iResult, iStatus); // async
	SetActive();
	}


void CActiveNameRequester::PrintResults()
	{
	TInquirySockAddr& sa = TInquirySockAddr::Cast(iResult().iAddr);
	const TBTDevAddr& bdaddr = sa.BTAddr();

	TBTDevAddrBuf bdaddrbuf;
	bdaddr.GetReadable(bdaddrbuf);

	iHROutputConsole->Printf(_L("Result: BdAddr = 0x%S"),&bdaddrbuf);

	if(sa.ResultFlags() & TInquirySockAddr::ERssiValid) 
		{
		iHROutputConsole->Printf(_L(" RSSI: %d"), sa.Rssi()); 
		}

	iHROutputConsole->Printf(_L("\n")); 

	TBuf<255> buf;
	TBuf8<255> buf8;
	TBool isNameComplete = EFalse;
	TInt error = KErrNone;

	if(iEirInstead)
		{
		TBluetoothNameRecordWrapper eir(iResult());

		iHROutputConsole->Printf(_L("*** EIR START ***\n"));
		
		error = eir.GetDeviceName(buf, isNameComplete);
		if(error == KErrNone)
			{
			if(isNameComplete == EFalse)
				{
				iHROutputConsole->Printf(_L("Partial Name: %S"), &buf);
				}
			else
				{
				iHROutputConsole->Printf(_L("Partial Name: %S"), &buf);
				}
			}
			
		if(eir.GetVendorSpecificDataLength() > 0)
			{
			eir.GetVendorSpecificData(buf8);
			if(buf.Size() > 0)
				{
				iHROutputConsole->Printf(_L("Manufacturer Specific Data: %S"), &buf8);
				}
			}
			
		TInt8 txPowerLevel;
		TInt err = txPowerLevel = eir.GetTxPowerLevel(txPowerLevel);
		if(err == KErrNone)
			{
			iHROutputConsole->Printf(_L("Tx Power Level: %d"), &txPowerLevel);
			}

		iHROutputConsole->Printf(_L("*** EIR END ***\n"));
		// Test EIR methods
		RArray<TUUID> uuids;
		
		TInt error = eir.GetDeviceName(buf, isNameComplete);
		iHROutputConsole->Printf(_L("*** GetLocalName returned: %d\n"), error);
		if(error >= KErrNone)
			{
			iHROutputConsole->Printf(_L("*** Retrieved Local Name: %S\n"),&buf);
			}
		
		RExtendedInquiryResponseUUIDContainer uuidContainer;
		error = eir.GetServiceClassUuids(uuidContainer);
		iHROutputConsole->Printf(_L("*** GetUUIDs returned: %d\n"), error);
		if(error >= KErrNone)
			{
			iHROutputConsole->Printf(_L("*** UUID Count: %d\n"), uuids.Count());
			TInt i;
			for(i=0;i<uuids.Count();i++)
				{
				iHROutputConsole->Printf(_L("*** UUID #%d minsz: %d value: 0x"), i, uuids[i].MinimumSize());
				TInt j;
				TPtrC8 uuid(uuids[i].ShortestForm());
				for(j=0;j<uuid.Length();j++)
					{
					iHROutputConsole->Printf(_L("%02X"), uuid[j]);
					}
				
				iHROutputConsole->Printf(_L(" \n"));
				}
			}

		}
	else
		{
		if (iResult().iName != KNullDesC)
			buf.Copy(iResult().iName);
		else
			buf = _L("NOT AVAILABLE");

		iHROutputConsole->Printf(_L("Result: Remote Name = \"%S\""),&buf);
		if(iResult().iFlags & TNameRecord::EPartial)
				{
				iHROutputConsole->Printf(_L(" [PARTIAL]\n"));
				}
			else
				{
				iHROutputConsole->Printf(_L(" [COMPLETE]\n"));
				}

		}

	TInt majorCoD = sa.MajorClassOfDevice();
	if (majorCoD & (1 << 13))
		iHROutputConsole->Printf(_L("Device is Limited Discoverable\n"));
	}

void CActiveNameRequester::RunL()
	{
	if (iStatus.Int() == KErrNone)
		{
		PrintResults();
		}
	else
		{
		iHROutputConsole->Printf(_L("GetByAddress failed with code %d\r\n"), iStatus.Int());
		}
	}

// ----------------------------------------------------------------------------
//							Active Name Array Requester
// ----------------------------------------------------------------------------


CActiveNameArrayRequester* CActiveNameArrayRequester::NewL(CConsoleBase& aConsole, CEirApp& aApp, RArray<TBTDevAddr>& aNameArray)
	{
	CActiveNameArrayRequester* namesreq = new (ELeave) CActiveNameArrayRequester(aConsole, aApp, aNameArray);
	CleanupStack::PushL(namesreq);
	namesreq->ConstructL();
	CleanupStack::Pop();
	return namesreq;
	}

CActiveNameArrayRequester::CActiveNameArrayRequester(CConsoleBase& aConsole, CEirApp& aApp, RArray<TBTDevAddr>& aNameArray)
: CActiveNameRequester(aConsole, aApp, EFalse, EFalse), iNameArray(aNameArray)
	{
	}

void CActiveNameArrayRequester::RunL()
	{
	if (iStatus.Int() == KErrNone || iStatus.Int() == KHCIErrorBase-EPageTimedOut)
		{
		PrintResults();
		// do next one if present
		if (iCurrentNameIndex < iNameArray.Count()-1)
			{
			++iCurrentNameIndex;
			DoSingleNameRequest();
			}
		}
	else
		{
		iHROutputConsole->Printf(_L("GetByAddress failed with code %d\n"), iStatus.Int());
		}
	}

void CActiveNameArrayRequester::ConstructL()
	{
	CActiveHostResolver::ConstructL();

	iInquiryRequest.SetAction(KHostResName);			// NAME ONLY
	iCurrentNameIndex = 0;
	Start();
	}

void CActiveNameArrayRequester::DoSingleNameRequest()
// FIXME make a member of NameRequester and sort out address ownsership
	{
	TBTDevAddr& currentAddr = iNameArray[iCurrentNameIndex];
	iInquiryRequest.SetBTAddr(currentAddr); // this object does the global addr for the moment

	TBTDevAddrBuf bdaddrbuf;
	currentAddr.GetReadable(bdaddrbuf);

	iHROutputConsole->Printf(_L("Looking up address %S...\n"),&bdaddrbuf);

	iHostResolver.GetByAddress(iInquiryRequest, iResult, iStatus); // async
	SetActive();
	}

void CActiveNameArrayRequester::PrintResults()
	{
	TInquirySockAddr& sa = TInquirySockAddr::Cast(iResult().iAddr);
	const TBTDevAddr& bdaddr = sa.BTAddr();

	TBTDevAddrBuf bdaddrbuf;
	bdaddr.GetReadable(bdaddrbuf);

	TBuf<255> buf;

	if (iResult().iName != KNullDesC)
		buf.Copy(iResult().iName);
	else
		buf = _L("NOT AVAILABLE");

	iHROutputConsole->Printf(_L("Result [HostResolver 0x%08x, Bdaddr %S]: Name: %S \n"),
							&iHostResolver, &bdaddrbuf, &buf);

	}

void CActiveNameArrayRequester::Start()
	{
	DoSingleNameRequest();
	}

// ----------------------------------------------------------------------------
//							Active Console
// ----------------------------------------------------------------------------


CActiveConsole::CActiveConsole(CConsoleBase& aConsole, CEirApp& aApp)
: CActive(EPriorityStandard), iConsole(aConsole), iApp(aApp)
	{
	}


CActiveConsole::~CActiveConsole()
	{
	// must cancel
	Cancel();
	}

CActiveConsole* CActiveConsole::NewL(CConsoleBase& aConsole, CEirApp& aApp)
	{
	CActiveConsole* console = new (ELeave) CActiveConsole(aConsole, aApp);
	CleanupStack::PushL(console);
	console->ConstructL();
	CleanupStack::Pop();
	return console;
	}

void CActiveConsole::ConstructL()
	{
	CActiveScheduler::Add(this);
	TestMenu();
	RequestKey();
	}

void CActiveConsole::DoCancel()
	{
	iConsole.ReadCancel();
	}

void CActiveConsole::RequestKey()
	{
	DrawCursor();
	iConsole.Read(iStatus);
	SetActive();
	}

void CActiveConsole::DrawCursor()
	{
	iConsole.Printf(KNullDesC());
	}

void CActiveConsole::TestMenu()
	{
	iConsole.Printf(_L("Choose action...\n\n"));
	iConsole.Printf(_L("n.\t\tSingle Name Request\n"));
	iConsole.Printf(_L("N.\t\tSingle Name Request; ignore Cache\n"));

	iConsole.Printf(_L("e.\t\tSingle EIR Request\n"));
	iConsole.Printf(_L("E.\t\tSingle EIR Request; ignore Cache\n"));

	iConsole.Printf(_L("i.\t\tGeneral Inquiry no Names\n"));
	iConsole.Printf(_L("I.\t\tGeneral Inquiry no Names; ignore Cache\n"));

	iConsole.Printf(_L("w.\t\tGeneral Inquiry with Names\n"));
	iConsole.Printf(_L("W.\t\tGeneral Inquiry with Names; ignore Cache\n"));

	iConsole.Printf(_L("x.\t\tGeneral Inquiry with EIR\n"));
	iConsole.Printf(_L("X.\t\tGeneral Inquiry with EIR; ignore Cache\n"));

	iConsole.Printf(_L("c.\t\tCancel Inquiry\n"));

	iConsole.Printf(_L("f.\t\tForever: general Inquiry with EIR; ignore Cache\n"));

	iConsole.Printf(_L("------------------------------------\n"));

	iConsole.Printf(_L("1.\t\tAdd EIR Publisher: Local Name\n"));
	iConsole.Printf(_L("2.\t\tAdd new EIR Publisher: UUID 16\n"));
	iConsole.Printf(_L("3.\t\tAdd new EIR Publisher: UUID 128\n"));
	iConsole.Printf(_L("4.\t\tAdd EIR Publisher: Manufacturer Specific\n"));

	iConsole.Printf(_L("Esc.\tStop\n"));
	}

void CActiveConsole::RunL()
	{
	// key has been pressed
	TChar ch = iConsole.KeyCode();
	TBool requestNextKey = ProcessKey(ch);
	if ( requestNextKey )
		{
		RequestKey();
		}
	}
	
TBool CActiveConsole::ProcessKey(TChar aCh)
	{
	TBool requestNextKey = ETrue;
	TInt err;
	switch (aCh)
		{
	case 'n':
		iConsole.Printf(_L("Starting name request...\n"));
		TRAP(err, iApp.NewNameRequesterL(EFalse, EFalse));
		if(err!=KErrNone)
			iConsole.Printf(_L("name request failed - %d"), err);
		break;

	case 'N':
		iConsole.Printf(_L("Starting name request (ignoring cache)...\n"));
		TRAP(err, iApp.NewNameRequesterL(EFalse, ETrue));
		if(err!=KErrNone)
			iConsole.Printf(_L("name request (ignoring cache) failed - %d"), err);
		break;

	case 'e':
		iConsole.Printf(_L("Starting EIR request...\n"));
		TRAP(err, iApp.NewNameRequesterL(ETrue, EFalse));
		if(err!=KErrNone)
			iConsole.Printf(_L("EIR request failed - %d"), err);
		break;

	case 'E':
		iConsole.Printf(_L("Starting EIR request (ignoring cache)...\n"));
		TRAP(err, iApp.NewNameRequesterL(ETrue, ETrue));
		if(err!=KErrNone)
			iConsole.Printf(_L("EIR request (ignoring cache) failed - %d"), err);
		break;

	case 'i':
		iConsole.Printf(_L("Starting General Inquiry no names...\n"));
		TRAP(err, iApp.NewInquirerL(KGIAC, EFalse, EFalse, EFalse));
		if(err!=KErrNone)
			iConsole.Printf(_L("General Inquiry failed - %d"), err);
		break;

	case 'I':
		iConsole.Printf(_L("Starting General Inquiry, no names, ignoring cache...\n"));
		TRAP(err, iApp.NewInquirerL(KGIAC, EFalse, ETrue, EFalse));
		if(err!=KErrNone)
			iConsole.Printf(_L("General Inquiry, no names, ignoring cache failed - %d"), err);
		break;


	case 'w':
		iConsole.Printf(_L("Starting General Inquiry with Names...\n"));
		TRAP(err, iApp.NewInquirerL(KGIAC, ETrue, EFalse, EFalse));
		if(err!=KErrNone)
			iConsole.Printf(_L("General Inquiry with Names failed - %d"), err);
		break;

	case 'W':
		iConsole.Printf(_L("Starting General Inquiry with names and no cache...\n"));
		TRAP(err, iApp.NewInquirerL(KGIAC, ETrue, ETrue, EFalse));
		if(err!=KErrNone)
			iConsole.Printf(_L("General Inquiry with names, ignoring cache failed - %d"), err);
		break;

	case 'x':
		iConsole.Printf(_L("Starting General Inquiry With EIR...\n"));
		TRAP(err, iApp.NewInquirerL(KGIAC, EFalse, EFalse, ETrue));
		if(err!=KErrNone)
			iConsole.Printf(_L("General Inquiry with EIR failed - %d"), err);
		break;

	case 'X':
		iConsole.Printf(_L("Starting General Inquiry With EIR, ignoring cache...\n"));
		TRAP(err, iApp.NewInquirerL(KGIAC, EFalse, ETrue, ETrue));
		if(err!=KErrNone)
			iConsole.Printf(_L("General Inquiry with EIR, ignoring cache failed - %d"), err);
		break;

	case 'a':
		iConsole.Printf(_L("Starting General Inquiry With both Name request and EIR...\n"));
		TRAP(err, iApp.NewInquirerL(KGIAC, ETrue, EFalse, ETrue));
		if(err!=KErrNone)
			iConsole.Printf(_L("General Inquiry with EIR failed - %d"), err);
		break;

	case 'A':
		iConsole.Printf(_L("Starting General Inquiry With both Name request and EIR, ignoring cache...\n"));
		TRAP(err, iApp.NewInquirerL(KGIAC, ETrue, ETrue, ETrue));
		if(err!=KErrNone)
			iConsole.Printf(_L("General Inquiry with EIR, ignoring cache failed - %d"), err);
		break;

	case 'c':
		// fixme - would be nice to enumerate the one to cancel
		// and see what happens to the others...
		iConsole.Printf(_L("Cancelling inquiry...\n"));
		iApp.CancelInquirer();
		break;

	case 'f':
		iConsole.Printf(_L("Neverending inquiry...\n"));
		TRAP(err, iApp.NewInquirerL(KGIAC, EFalse, ETrue, ETrue, ETrue));
		break;

	case '1':
/*		iConsole.Printf(_L("Change Local Name...\n"));
		TRAP(err, iApp.NewEirPublisherTestL(ETestDeviceName));
		if(err!=KErrNone)
			iConsole.Printf(_L("failed - %d"), err);
*/
		iConsole.Printf(_L("to be supported in teir"));
		break;

	case '2':
		iConsole.Printf(_L("Creating an EIR UUID16 publisher...\n"));
		TRAP(err, iApp.NewEirPublisherTestL(ETestUuid16));
		if(err!=KErrNone)
			iConsole.Printf(_L("failed - %d"), err);
		break;

	case '3':
		iConsole.Printf(_L("Creating an EIR UUID128 publisher...\n"));
		TRAP(err, iApp.NewEirPublisherTestL(ETestUuid128));
		if(err!=KErrNone)
			iConsole.Printf(_L("failed - %d"), err);
		break;

	case '4':
		iConsole.Printf(_L("Creating an EIR Manufacturer Specific publisher...\n"));
		TRAP(err, iApp.NewEirPublisherTestL(ETestSetMSD));
		if(err!=KErrNone)
			iConsole.Printf(_L("failed - %d"), err);
		break;

	case '?':
		TestMenu();
		break;

	case EKeyEscape:
		iApp.Stop(); // don't want another key press
		requestNextKey = EFalse;
		break;

	default:
		iConsole.Printf(_L("Unknown command\r\n"));
		}
	
	return requestNextKey;
	}

TInt CActiveConsole::SetHostName()
	{
	test.Printf(_L("Set Host Name to: "));
	TKeyCode key;
	TBuf16<255> buf;
	
	while((key = test.Getch())!=EKeyEnter && buf.Length()<buf.MaxLength())
		{
		if(key == EKeyBackspace&&buf.Length()!=0)
			{
			buf.SetLength(buf.Length()-1);
			}
		else 
			{
			buf.Append(key);
			}
		
		test.Printf(_L("%c"), key);
		}
		
//	iHostResolver.SetHostName(buf);
	test.Printf(_L("\n"));
	return KErrNone;
	}

CManufacturerDataNotifier* CManufacturerDataNotifier::NewL(CEirApp& aEirApp)
	{
	CManufacturerDataNotifier* self = new (ELeave) CManufacturerDataNotifier(aEirApp);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop();
	return self;
	}

CManufacturerDataNotifier::CManufacturerDataNotifier(CEirApp& aEirApp)
: iEirApp(aEirApp)
	{
	}
	
CManufacturerDataNotifier::~CManufacturerDataNotifier()
	{
	}
	
void CManufacturerDataNotifier::ConstructL()
	{
	}
	
// From MManufacturerSpecificDataPublisherNotifier
void CManufacturerDataNotifier::MmsdpnNotifyAvailableDataSize(TUint aAvailableDataSize)
	{
	iEirApp.SetPublishManuData(aAvailableDataSize);
	}

void CManufacturerDataNotifier::MmsdpnNotifyError(TInt /*aResult*/)
	{
	// Check aResult == KErrNone
	}


void StartL()
	{
	CEirApp* app = CEirApp::NewL(*test.Console());
	CleanupStack::PushL(app);
	app->StartL();
	CleanupStack::PopAndDestroy(app);
	}

TInt E32Main()
	{
	TInt ret;
	test.SetLogged(EFalse);
	test.Title();

// For some reason, you have to do the following to
// ensure that the file server behaves properly.
	RFs fs;
	ret=fs.Connect();
	fs.Close();
// Run through tests

	__UHEAP_MARK;
	CTrapCleanup* cleanupStack=CTrapCleanup::New();	// Get CleanupStack

	CActiveScheduler* activescheduler=new CActiveScheduler;
	CActiveScheduler::Install(activescheduler);

	TRAPD(err, StartL());

	delete activescheduler;
	delete cleanupStack;
	__UHEAP_MARKEND;

	return err;
	}
