--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/eirman/eirmanager.cpp Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,756 @@
+// Copyright (c) 2007-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
+*/
+
+#include "eirmanager.h"
+#include <bluetooth/hcicommandqueue.h>
+#include <bttypes.h>
+#include <bluetooth/hci/commandcompleteevent.h>
+#include <bluetooth/hci/commandstatusevent.h>
+#include <bluetooth/hci/command.h>
+#include <bluetooth/hci/hciconsts.h>
+#include <bluetooth/hci/writeextendedinquiryresponsecommand.h>
+#include <bluetooth/logger.h>
+#include "eirmansession.h"
+#include "linkmgr.h"
+#include "eirmanpanics.h"
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_EIRMANAGER);
+#endif
+
+__ASSERT_COMPILE(KReservedNameBytes + KReservedUuid16Bytes + KReservedUuid128Bytes + KReservedManufacturerSpecificDataBytes + KReservedTxPowerLevelBytes == KEirPacketSize);
+
+CEirManager* CEirManager::NewL(MHCICommandQueue& aCommandQueue, CLinkMgrProtocol& aLinkMgrProtocol)
+ {
+ LOG_STATIC_FUNC
+
+ CEirManager* self = new(ELeave) CEirManager(aCommandQueue, aLinkMgrProtocol);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ CleanupStack::Pop(self);
+ return self;
+ }
+
+CEirManager::~CEirManager()
+ {
+ LOG_FUNC
+
+ // Delete all registered tags
+ for(TInt8 i = static_cast<TInt8>(EEirTagName); i < static_cast<TInt8>(EEirTagRESERVED); i++)
+ {
+ __ASSERT_DEBUG(!iNotifiees[i].iNotifiee, EIR_MANAGER_PANIC(EEirManagerNotifieeNotCleared));
+ }
+
+ iCommandQueue.MhcqRemoveAllCommands(*this);
+ delete iWriteEirTimer;
+ }
+
+CEirManager::CEirManager(MHCICommandQueue& aCommandQueue, CLinkMgrProtocol& aLinkMgrProtocol)
+ : iOffersStale(EFalse)
+ , iCmdId(KInvalidCommandQueueItemId)
+ , iCmdCount(0)
+ , iCommandQueue(aCommandQueue)
+ , iLinkMgrProtocol(aLinkMgrProtocol)
+ {
+ LOG_FUNC
+ }
+
+void CEirManager::ConstructL()
+ {
+ LOG_FUNC
+ iWriteEirTimer = CWriteEirTimer::NewL(*this);
+ }
+
+void CEirManager::UnhookClient(TEirTag aTag)
+ {
+ LOG_FUNC
+ if (iNotifiees[aTag].iNotifiee)
+ {
+ iNotifiees[aTag].iNotifiee = NULL;
+ if(iNotifiees[aTag].iTagData)
+ {
+ delete iNotifiees[aTag].iTagData;
+ iNotifiees[aTag].iTagData = NULL;
+ }
+ }
+ }
+
+// A client register aTag with EirManager
+TInt CEirManager::RegisterTag(TEirTag aTag, MEirManagerNotifiee& aNotifiee)
+ {
+ LOG_FUNC
+ TInt ret = KErrNone;
+ LOG1(_L("CEirManager::RegisterTag tag = %d"), aTag);
+
+ if(!TagValid(aTag))
+ {
+ LOG1(_L("CEirManager::RegisterTag INVALID TAG: %d"), aTag);
+ ret = KErrArgument;
+ }
+ else if(!TagAllowed(aTag))
+ {
+ LOG(_L("CEirManager::RegisterTag: IN USE"));
+ ret = KErrInUse;
+ }
+ else
+ {
+ iNotifiees[aTag].iNotifiee = &aNotifiee;
+ iNotifiees[aTag].iTagData = NULL;
+ iNotifiees[aTag].iOfferedBytes = 0;
+ iNotifiees[aTag].iEirDataMode = EEirDataComplete;
+ iNotifiees[aTag].iState = 0x00;
+
+ if (!OffersPending() && NeedToOffer())
+ {
+ // Do not consider this for insertion yet if there are offers pending.
+ // Instead, once the offers have been responded to, this client will be dealt with
+ NotifyClients();
+ }
+ }
+ return ret;
+ }
+
+// A client deregister aTag with EirManager
+void CEirManager::DeregisterTag(TEirTag aTag)
+ {
+ LOG_FUNC
+ LOG1(_L("CEirManager::DeregisterTag tag = %d"), aTag);
+
+ if(!TagValid(aTag))
+ {
+ LOG1(_L("CEirManager::DeregisterTag INVALID TAG: %d"), aTag);
+ return;
+ }
+
+ if (iNotifiees[aTag].iNotifiee)
+ {
+ UnhookClient(aTag);
+ if(iNotifiees[aTag].iRequiredBytes > 0)
+ {
+ // It did require some bytes and now requires none, reevaluate offers
+ iNotifiees[aTag].iState &= ~KNeedOffer;
+ // A client could storm in a deregister before setting data - so we remove
+ // any pending offer as a SetData after deregistering will be errored - any
+ // previous offer has been voided.
+ iNotifiees[aTag].iState &= ~KOfferPending;
+ if(!OffersPending())
+ {
+ NotifyClients();
+ }
+ else if(iNotifiees[aTag].iOfferedBytes > 0)
+ {
+ iOffersStale = ETrue;
+ }
+ }
+ if(!NeedToOffer() && !OffersPending())
+ {
+ TryToUpdateHostController();
+ }
+ // Now tidy up offer and requested values (they are no longer valid).
+ iNotifiees[aTag].iRequiredBytes = 0;
+ iNotifiees[aTag].iOfferedBytes = 0;
+ }
+ }
+
+TInt CEirManager::SetData(TEirTag aTag, TDesC8& aData, TEirDataMode aEirDataMode)
+ {
+ LOG_FUNC
+ LOG1(_L("CEirManager::SetData tag = %d"), aTag);
+
+ if(!iLinkMgrProtocol.IsExtendedInquiryResponseSupportedLocally())
+ {
+ return KErrNotSupported;
+ }
+
+ if(!TagValid(aTag))
+ {
+ LOG1(_L("CEirManager::SetData INVALID TAG: %d"), aTag);
+ return KErrArgument;
+ }
+
+ if(!iNotifiees[aTag].iNotifiee)
+ {
+ // No client registered with this tag
+ return KErrNotFound;
+ }
+
+ LOG2(_L("CEirManager::SetData data len = %d offered bytes: %d"), aData.Length(), iNotifiees[aTag].iOfferedBytes);
+
+ if (!(iNotifiees[aTag].iState & KOfferPending))
+ {
+ // No offer pending for this tag
+ return KErrNotReady;
+ }
+ if(iNotifiees[aTag].iOfferedBytes < aData.Length())
+ {
+ // Client wants to publish more data than allowed to
+ return KErrTooBig;
+ }
+
+ TInt ret = KErrNone;
+
+ HBufC8** tagdata;
+
+ FindTagData(aTag, tagdata);
+
+ // Free up whatever there was before
+ if (*tagdata)
+ {
+ delete *tagdata;
+ }
+
+ HBufC8* data = NULL;
+
+ // If the descriptor is empty, mark the heap buffer pointer as NULL,
+ // meaning that the client is registered for this tag, but there is no currently no data available for it
+ if(aData.Length() != 0x0)
+ {
+ data = aData.Alloc();
+ if (!data)
+ {
+ LOG(_L("CEirManager::SetData: NO MEMORY "));
+ ret = KErrNoMemory;
+ }
+ }
+
+ *tagdata = data;
+
+ // We no longer have a value pending.
+ iNotifiees[aTag].iState &= ~KOfferPending;
+ iNotifiees[aTag].iEirDataMode = aEirDataMode;
+
+ if(NeedToOffer() && !OffersPending())
+ {
+ NotifyClients();
+ }
+
+ if(!NeedToOffer() && !OffersPending())
+ {
+
+ // Regardless of whether the allocation failed or not, check whether all offers have been accepted
+ // and update the Host Controller accordingly
+ TryToUpdateHostController();
+ }
+
+ LOG1(_L("CEirManager::SetData returning = %d"), ret);
+ return ret;
+ }
+
+void CEirManager::MhcqcCommandEventReceived(const THCIEventBase& aEvent,
+ const CHCICommandBase* /*aRelatedCommand*/)
+ {
+ LOG_FUNC
+ if(aEvent.EventCode() == ECommandCompleteEvent)
+ {
+ const THCICommandCompleteEvent& completeEvent = static_cast<const THCICommandCompleteEvent&>(aEvent);
+ if(completeEvent.CommandOpcode() == KWriteExtendedInquiryResponseOpcode)
+ {
+ --iCmdCount;
+ }
+ else
+ {
+ LOG1(_L("Warning!! Unexpected Command Complete Event Received (command opcode: 0x%04x)"), completeEvent.CommandOpcode());
+ __ASSERT_DEBUG(EFalse, EIR_MANAGER_PANIC(EEirManagerUnexpectedCompleteEvent));
+ }
+ }
+ else
+ {
+ LOG1(_L("Warning!! Unexpected Event Received (event code: 0x%02x)"), aEvent.EventCode());
+ __ASSERT_DEBUG(EFalse, EIR_MANAGER_PANIC(EEirManagerUnexpectedEvent));
+ }
+ }
+
+void CEirManager::MhcqcCommandErrored(TInt aErrorCode, const CHCICommandBase* aCommand)
+ {
+ LOG_FUNC
+ aErrorCode = aErrorCode; // Avoid unused warning
+ LOG1(_L("\taErrorCode = %d"), aErrorCode);
+ if(aCommand && aCommand->Opcode() == KWriteExtendedInquiryResponseOpcode)
+ {
+ --iCmdCount;
+ }
+ else
+ {
+ LOG(_L("Warning!! Unexpected Error Received"));
+ if(aCommand)
+ {
+ LOG1(_L("\taCommand->Opcode() = 0x%04x"), aCommand->Opcode());
+ }
+ }
+ }
+
+void CEirManager::WriteExtendedInquiryResponseL(TBool aFECRequired, const TDesC8& aEir)
+ {
+ LOG_FUNC
+ if(iCmdCount > 0)
+ {
+ // There is at least 1 outstanding command in the queue
+ TInt err = iCommandQueue.MhcqRemoveCommand(iCmdId, *this);
+ if(err == KErrNone)
+ {
+ --iCmdCount;
+ }
+ // else if there was an error it is because the command cannot be removed
+ // "at this time" so we just "leak" it. This isn't a problem, this was a
+ // best effort optimisation.
+ }
+ CWriteExtendedInquiryResponseCommand* cmd = CWriteExtendedInquiryResponseCommand::NewL(aFECRequired ? 1 : 0, aEir);
+ // Ownership of cmd transfered
+ iCmdId = iCommandQueue.MhcqAddCommandL(cmd, *this);
+ ++iCmdCount;
+ }
+
+// Check whether a specific tag is allowed to be registered
+TBool CEirManager::TagAllowed(TEirTag aTag) const
+ {
+ LOG_FUNC
+ // We don't support 32 bit UUIDs
+ return (aTag!=EEirTagSdpUuid32);
+ }
+
+// Check whether aTag is present
+TBool CEirManager::TagPresent(TEirTag aTag) const
+ {
+ LOG_FUNC
+ return (iNotifiees[aTag].iNotifiee != NULL);
+ }
+
+// Find the registered data for aTag
+void CEirManager::FindTagData(TEirTag aTag, HBufC8**& aOutPointerToData)
+ {
+ LOG_FUNC
+ aOutPointerToData = &iNotifiees[aTag].iTagData;
+ }
+
+// Count the number of registered tags
+TInt CEirManager::TagCount() const
+ {
+ LOG_FUNC
+ TInt count = 0;
+ for (TInt i=0; i<EEirTagRESERVED; i++)
+ {
+ if (iNotifiees[i].iNotifiee)
+ {
+ count++;
+ }
+ }
+ return count;
+ }
+
+TBool CEirManager::OffersPending() const
+ {
+ LOG_FUNC
+ for (TInt i=0; i<EEirTagRESERVED; i++)
+ {
+ if(iNotifiees[i].iState & KOfferPending)
+ {
+ return ETrue;
+ }
+ }
+ return EFalse;
+ }
+
+TBool CEirManager::NeedToOffer() const
+ {
+ LOG_FUNC
+ if(iOffersStale)
+ {
+ return ETrue;
+ }
+ for (TInt i=0; i<EEirTagRESERVED; i++)
+ {
+ if (iNotifiees[i].iState & KNeedOffer)
+ {
+ return ETrue;
+ }
+ }
+ return EFalse;
+ }
+
+// Notify EirManager there is a change of data from aTag client
+TInt CEirManager::NewData(TEirTag aTag, TInt aRequiredBytes)
+ {
+ LOG_FUNC
+ if(!iLinkMgrProtocol.IsExtendedInquiryResponseSupportedLocally())
+ {
+ return KErrNotSupported;
+ }
+
+ // including 2 bytes for length and tag
+ TInt requiredBytes = 0;
+ TInt currentBytes = iNotifiees[aTag].iRequiredBytes;
+ if(aRequiredBytes > 0)
+ {
+ requiredBytes = aRequiredBytes + KEirLengthTagLength;
+ }
+ else if(aTag == EEirTagName && aRequiredBytes == 0)
+ {
+ // This is a special case for device without local name
+ requiredBytes = KEirLengthTagLength;
+ }
+ else
+ {
+ requiredBytes = 0;
+ }
+
+ iNotifiees[aTag].iRequiredBytes = requiredBytes; // always record what we last asked for.
+
+ if(!(iNotifiees[aTag].iState & KNeedOffer))
+ {
+ // We currently aren't marked as needing an offer so we will need to fix that...
+ iNotifiees[aTag].iState |= KNeedOffer;
+ if(requiredBytes == currentBytes && !(iNotifiees[aTag].iState & KOfferPending) && !iOffersStale)
+ {
+ // If the same then we handle locally, if we aren't already waiting.
+ DoNotify(aTag);
+ }
+ else if(!OffersPending())
+ {
+ // If not waiting for anybody then lets kick off a round.
+ NotifyClients();
+ }
+ }
+ return KErrNone;
+ }
+
+// Notify all registered clients about their allocated data length
+void CEirManager::NotifyClients()
+ {
+
+ LOG_FUNC
+
+ // go around each notifiee and ask for values
+ // our policy at the moment is:
+ // after allocating reserved bytes for each tag, we will prioritize tag with big granularity
+
+ ComputeOfferLengths();
+
+ for (TInt i = 0; i<EEirTagRESERVED; i++)
+ {
+ if(TagPresent((TEirTag) i))
+ {
+ DoNotify((TEirTag)i);
+ }
+ }
+ }
+
+// Notify each tag about the available data length
+void CEirManager::DoNotify(TEirTag aTag)
+ {
+ LOG_FUNC
+
+ iNotifiees[aTag].iState &= ~KNeedOffer; // We got an offer!
+
+ // Only make valid offers: offers more than 0 and 0 offers with non 0 request
+ if(iNotifiees[aTag].iOfferedBytes > 0)
+ {
+ // Notify client about offered bytes
+ iNotifiees[aTag].iState |= KOfferPending;
+ iNotifiees[aTag].iNotifiee->MemnEirBlockAvailable(aTag, iNotifiees[aTag].iOfferedBytes);
+ }
+ else if(iNotifiees[aTag].iOfferedBytes == 0)
+ {
+ if(iNotifiees[aTag].iRequiredBytes > 0)
+ {
+ // Notify client about 0 offer and not expecting client to call back
+ iNotifiees[aTag].iState |= KOfferPending;
+ iNotifiees[aTag].iNotifiee->MemnEirBlockAvailable(aTag, iNotifiees[aTag].iOfferedBytes);
+ }
+ else if(iNotifiees[aTag].iRequiredBytes == 0)
+ {
+ // request 0 and offered 0, delete client
+ iNotifiees[aTag].iState &= ~KNeedOffer;
+ delete iNotifiees[aTag].iTagData;
+ iNotifiees[aTag].iTagData = NULL;
+ }
+ }
+ }
+
+void CEirManager::ComputeOfferLengths()
+ {
+ LOG_FUNC
+ // count bytes available for EIR data, including length and tag
+ // due to the low cost of Tx Power Level, it is always given a space in EIR
+ TInt availableEirSize = EIRPacketSize();
+ TInt reservedByteForName = KReservedNameBytes - (KEirPacketSize - availableEirSize);
+ TInt totalSpare = 0;
+
+ ComputeClientInitialOffer(EEirTagName, totalSpare, reservedByteForName);
+ ComputeClientInitialOffer(EEirTagSdpUuid16, totalSpare, KReservedUuid16Bytes);
+ ComputeClientInitialOffer(EEirTagSdpUuid128, totalSpare, KReservedUuid128Bytes);
+ ComputeClientInitialOffer(EEirTagManufacturerSpecific, totalSpare, KReservedManufacturerSpecificDataBytes, ETrue);
+ ComputeClientInitialOffer(EEirTagTxPowerLevel, totalSpare, KReservedTxPowerLevelBytes, ETrue);
+
+ // if there is still bytes left after allocating the reserved bytes,
+ // we will try to allocate them by giving high priority to tag with bigger granularity
+ /* tag granularity (byte)
+ name 1
+ uuid16 2
+ uuid128 16
+ manufacturer length
+ */
+ LOG4(_L("RequiredBytes **** Name: %d, UUID16: %d, UUID128: %d, Manu: %d"),
+ iNotifiees[0].iRequiredBytes, iNotifiees[1].iRequiredBytes, iNotifiees[3].iRequiredBytes, iNotifiees[4].iRequiredBytes);
+ LOG4(_L("OfferedBytes ----- Name: %d, UUID16: %d, UUID128: %d, Manu: %d"),
+ iNotifiees[0].iOfferedBytes, iNotifiees[1].iOfferedBytes, iNotifiees[3].iOfferedBytes, iNotifiees[4].iOfferedBytes);
+ LOG1(_L("totalSpare: %d"), totalSpare);
+ ComputeClientFinalOffer(EEirTagManufacturerSpecific, totalSpare, KReservedManufacturerSpecificDataBytes, ETrue);
+ ComputeClientFinalOffer(EEirTagSdpUuid128, totalSpare, K128BitUuidGranularity);
+ ComputeClientFinalOffer(EEirTagSdpUuid16, totalSpare, K16BitUuidGranularity);
+ ComputeClientFinalOffer(EEirTagName, totalSpare, KDeviceNameGranularity);
+ // Tx Power Level should be after the allocating the reserved bytes
+ iOffersStale = EFalse; // We now know any "staleness" has been corrected.
+
+ LOG(_L("After allocating reserved"));
+ LOG4(_L("RequiredBytes **** Name: %d, UUID16: %d, UUID128: %d, Manu: %d"),
+ iNotifiees[0].iRequiredBytes, iNotifiees[1].iRequiredBytes, iNotifiees[3].iRequiredBytes, iNotifiees[4].iRequiredBytes);
+ LOG4(_L("OfferedBytes ----- Name: %d, UUID16: %d, UUID128: %d, Manu: %d"),
+ iNotifiees[0].iOfferedBytes, iNotifiees[1].iOfferedBytes, iNotifiees[3].iOfferedBytes, iNotifiees[4].iOfferedBytes);
+ LOG1(_L("totalSpare: %d"), totalSpare);
+ }
+
+/* compute offer based on client's request and the reserved bytes for this client*/
+void CEirManager::ComputeClientInitialOffer(TEirTag aTag, TInt& aTotalSpare, TInt aReservedBytes,
+ TBool aIsAllOrNothing)
+ {
+ if(TagPresent(aTag))
+ {
+ if(aIsAllOrNothing)
+ {
+ if(iNotifiees[aTag].iRequiredBytes <= aReservedBytes)
+ {
+ iNotifiees[aTag].iOfferedBytes = iNotifiees[aTag].iRequiredBytes;
+ aTotalSpare += (aReservedBytes - iNotifiees[aTag].iRequiredBytes);
+ iNotifiees[aTag].iEirDataMode = EEirDataComplete;
+ }
+ else
+ {
+ // the request exceeds reserved bytes for this client, the client is offered nothing
+ iNotifiees[aTag].iOfferedBytes = 0;
+ iNotifiees[aTag].iEirDataMode = EEirDataPartial;
+ aTotalSpare += aReservedBytes;
+ }
+ }
+ else
+ {
+ if(iNotifiees[aTag].iRequiredBytes <= aReservedBytes)
+ {
+ iNotifiees[aTag].iOfferedBytes = iNotifiees[aTag].iRequiredBytes;
+ aTotalSpare += (aReservedBytes - iNotifiees[aTag].iRequiredBytes);
+ iNotifiees[aTag].iEirDataMode = EEirDataComplete;
+ }
+ else
+ {
+ iNotifiees[aTag].iOfferedBytes = aReservedBytes;
+ iNotifiees[aTag].iEirDataMode = EEirDataPartial;
+ }
+ }
+ }
+ else
+ {
+ aTotalSpare += aReservedBytes;
+ }
+ }
+
+/* compute offer based on client's request and the spared bytes from the initial offers*/
+void CEirManager::ComputeClientFinalOffer(TEirTag aTag, TInt& aTotalSpare, TUint aGranularity,
+ TBool aIsAllOrNothing)
+ {
+ if(iNotifiees[aTag].iRequiredBytes > iNotifiees[aTag].iOfferedBytes)
+ {
+ if(aIsAllOrNothing)
+ {
+ // this means we have more data to publish, which also indicates
+ // it required bytes are more than reserved bytes for this tag
+ if(iNotifiees[aTag].iRequiredBytes <= aTotalSpare)
+ {
+ aTotalSpare -= iNotifiees[aTag].iRequiredBytes;
+ iNotifiees[aTag].iOfferedBytes = iNotifiees[aTag].iRequiredBytes;
+ iNotifiees[aTag].iEirDataMode = EEirDataComplete;
+ }
+ }
+ else
+ {
+ // this means we have more data to publish
+ if((iNotifiees[aTag].iRequiredBytes - iNotifiees[aTag].iOfferedBytes) <= aTotalSpare)
+ {
+ aTotalSpare -= (iNotifiees[aTag].iRequiredBytes - iNotifiees[aTag].iOfferedBytes);
+ iNotifiees[aTag].iOfferedBytes = iNotifiees[aTag].iRequiredBytes;
+ iNotifiees[aTag].iEirDataMode = EEirDataComplete;
+ }
+ else
+ {
+ iNotifiees[aTag].iOfferedBytes += (aTotalSpare - aTotalSpare % aGranularity);
+ aTotalSpare %= aGranularity;
+ }
+ }
+ }
+ }
+
+// Calculate the valid size of EIR packet, excluding payload header
+TInt CEirManager::EIRPacketSize() const
+ {
+ LOG_FUNC
+ TInt total = 0;
+ // default value of valid EIR packet is for using DM1
+ TInt ret = KEirPacketSize - KEirDM1PayloadHeaderSize;
+ for (TInt i = 0; i<EEirTagRESERVED; i++)
+ {
+ if(TagPresent((TEirTag) i) && iNotifiees[i].iRequiredBytes > 0)
+ {
+ total += iNotifiees[i].iRequiredBytes;
+ // Including 2 bytes for the length and tag
+ total += KEirLengthTagLength;
+ }
+ }
+
+ if(total > KEirDM1PacketSize)
+ {
+ // None DM1 packet has 2 bytes of payload header, recalculate...
+ ret = KEirPacketSize - KEirNoneDM1PayloadHeaderSize;
+ }
+
+ return ret;
+ }
+
+void CEirManager::UpdateHostControllerL()
+ {
+ LOG_FUNC
+
+ /* We don't handle the situation of handware swapping after stack is started
+ * when the device changed from 2.1 to 2.0 and EIR is no longer supports,
+ * we'll quietly stop writing EIR and return KErrNotSupported for future requests.
+ we
+ */
+ __ASSERT_DEBUG(iLinkMgrProtocol.IsExtendedInquiryResponseSupportedLocally(), EIR_MANAGER_PANIC(EEirManagerEirNotSupported));
+ TBuf8<KHCIExtendedInquiryResponseMaxLength> eir;
+
+ for (TInt i = 0; i<EEirTagRESERVED; i++)
+ {
+ TExtendedInquiryResponseDataType dataType = EEirLocalNamePartial;
+
+ // Skip non-registered and data-empty tags
+ if(iNotifiees[i].iNotifiee)
+ {
+ if(i == EEirTagName || iNotifiees[i].iTagData)
+ {
+ // We'll always try to publish empty device name according to the spec
+ switch (i)
+ {
+ // Name should go first
+ case EEirTagName:
+ dataType = iNotifiees[i].iEirDataMode == EEirDataPartial ? EEirLocalNamePartial : EEirLocalNameComplete;
+ break;
+
+ case EEirTagSdpUuid16:
+ dataType = iNotifiees[i].iEirDataMode == EEirDataPartial ? EEirUUID16Partial : EEirUUID16Complete;
+ break;
+
+ case EEirTagSdpUuid128:
+ dataType = iNotifiees[i].iEirDataMode == EEirDataPartial ? EEirUUID128Partial : EEirUUID128Complete;
+ break;
+
+ case EEirTagManufacturerSpecific:
+ dataType = EEirVendorSpecific;
+ break;
+
+ case EEirTagTxPowerLevel:
+ dataType = EEirTxPowerLevel;
+ break;
+ }
+ if(iNotifiees[i].iTagData)
+ {
+ // Data Structure length, add a byte for the Data Type
+ eir.Append(iNotifiees[i].iTagData->Length() + 1);
+ // Data Structure data: Data Type
+ eir.Append(dataType);
+ // Data Structure data: EIR data
+ eir.Append(*(iNotifiees[i].iTagData));
+ }
+ else
+ {
+ // Here is the case of empty device name again
+ eir.Append(1);
+ // Data Structure data: Data Type
+ eir.Append(dataType);
+ }
+ }
+ }
+ }
+
+ LOG1(_L("CEirManager::UpdateHostController about to send %d bytes of EIR data"), eir.Length());
+ // Send this down HCI
+ WriteExtendedInquiryResponseL(ETrue, eir);
+
+ LOG(_L("------- EIR verbose display"));
+ LOGHEXDESC(eir);
+ }
+
+void CEirManager::TryToUpdateHostController()
+ {
+ // In case there is an outstanding write eir command (a timer running) already, Cancel()
+ iWriteEirTimer->Cancel();
+
+ // Start the timer to gurad against from clients' updates flooding the controller
+ iWriteEirTimer->Start();
+ }
+
+
+CWriteEirTimer* CWriteEirTimer::NewL(CEirManager& aEirManager)
+ {
+ CWriteEirTimer* timer = new (ELeave) CWriteEirTimer(aEirManager);
+ CleanupStack::PushL(timer);
+ timer->ConstructL();
+ CleanupStack::Pop(timer);
+ return timer;
+ }
+
+
+CWriteEirTimer::CWriteEirTimer(CEirManager& aEirManager)
+: CTimer(EPriorityStandard), iEirManager(aEirManager)
+ {
+ }
+
+void CWriteEirTimer::Start()
+ {
+ After(KWriteEirTimeout);
+ }
+
+void CWriteEirTimer::ConstructL()
+ {
+ CTimer::ConstructL();
+ CActiveScheduler::Add(this);
+ }
+
+void CWriteEirTimer::RunL()
+ {
+ // timer expires, which means no more write eir request within life of the time (1 second)
+ // write eir here now
+ iEirManager.UpdateHostControllerL();
+ }
+
+TInt CWriteEirTimer::RunError(TInt IF_FLOGGING(aError))
+ {
+ LOG_FUNC
+ // The only reason the error occurs is that the WriteExtendedInquiryResponseL has failed
+ // This may be caused by invalid EIR data, we'll do nothing here.
+ LOG1(_L("CWriteEirTimer::RunError() %d: "), aError);
+ return KErrNone;
+ }