--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btsdp/server/epocsvr.cpp Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,1469 @@
+// Copyright (c) 2000-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:
+//
+
+#include <bluetooth/logger.h>
+#include <e32svr.h>
+#include <e32uid.h>
+#include <btsdp.h>
+#include "DataEncoder.h"
+#include "EncoderVisitor.h"
+#include "sdputil.h"
+#include "reqhandler.h"
+#include "listener.h"
+#include "epocsvr.h"
+#include "ipcinternals.h"
+#include "attrvalueencoded.h"
+
+#include "epocsvr.inl"
+#include <f32file.h> // For RFs
+
+#ifdef __EPOC32__
+#include <c32comm.h>
+#endif
+
+#include "SdpServerSecurityPolicy.h"
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_SDP_SERVER);
+#endif
+
+#ifdef _DEBUG
+PANICCATEGORY("epocsvr");
+#endif
+
+GLDEF_D TInt sdp_debug_level = 5;
+
+void Panic(TSdpServerPanics aCode)
+ {
+ User::Panic(KSdpServerPanicName, aCode);
+ }
+
+/**
+ The SDP database subsession class has been removed from the SDP server but to
+ maintain BC we still have to deal with subsession requests from the client.
+ The constant below is used as the subsession handle for any new subsession
+ requests from a client as we no longer maintain a real handle to a subsession.
+**/
+static const TInt KSdpDatabaseSubSessionHandle = 0x1A2B3C4D;
+
+inline CSdpServerShutdown::CSdpServerShutdown()
+ :CTimer(-1)
+ {CActiveScheduler::Add(this);}
+inline void CSdpServerShutdown::ConstructL()
+ {CTimer::ConstructL();}
+inline void CSdpServerShutdown::Start()
+ {After(KSdpServerShutdownDelay);}
+
+//
+
+
+void CSdpServerShutdown::RunL()
+/**
+ Initiate server exit when the timer expires
+**/
+ {
+ LOG_FUNC
+ CActiveScheduler::Stop();
+ }
+
+CSdpServer::CSdpServer()
+ : CPolicyServer(0, KSdpServerPolicy)
+ {
+ CONNECT_LOGGER
+ LOG_FUNC
+ }
+
+CSdpServer* CSdpServer::NewLC()
+/**
+ Create the server object and leave on cleanup stack.
+**/
+ {
+ LOG_STATIC_FUNC
+ CSdpServer* self=new(ELeave) CSdpServer();
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ return self;
+ }
+
+void CSdpServer::ConstructL()
+ {
+ LOG_FUNC
+ StartL(KSdpServerName);
+
+ iSdpDatabase = CSdpDatabase::NewL();
+ iUuidManager = CSdpUuidManager::NewL(*iSdpDatabase);
+
+ TPckg<TUint> intBuf(0);
+ iDbState = CSdpAttrValueUint::NewUintL(intBuf);
+
+ // Create the buffer the correct size for storing a Uint
+ TInt encodedSize = TElementEncoder::EncodedSize(iDbState->Type(), iDbState->DataSize());
+ iEncodeBuf.CreateL(encodedSize);
+ TElementEncoder encoder(iEncodeBuf);
+
+ iEncoderVisitor = CAttrEncoderVisitor::NewL(encoder);
+ BuildRecordZeroL(); //Unencoded
+ Database()->EncodeDbL(); /*If we get to here, database exists*/
+
+ User::LeaveIfError(iSocketServ.Connect());
+ iSdpListener = CSdpListener::NewL(iSocketServ, 4, *(Database()));
+
+ //Ensure that the server will exit even if the 1st client fails to connect
+ //Start shutdown timer only after all the heavy duty initialisation has been done.
+ //Otherwise it could fire before any client had a chance to connect to the server
+ iShutdown.ConstructL();
+ iShutdown.Start();
+ }
+
+CSdpServer::~CSdpServer()
+ {
+ LOG_FUNC
+ delete iSdpListener;
+ iSocketServ.Close();
+
+ delete iRecZero;
+ delete iDbState;
+ delete iEncoderVisitor;
+ iEncodeBuf.Close();
+
+ delete iUuidManager;
+ delete iSdpDatabase;
+ CLOSE_LOGGER
+ }
+
+CSdpDatabase* CSdpServer::Database()
+ {
+ LOG_FUNC
+ return iSdpDatabase;
+ }
+
+void CSdpServer::BuildRecordZeroL()
+/**
+ Record 0 should be used when a server instance is created.
+ note that attributes 2, 5 and 0x201 should be updated.
+ also note only English, pas de Francais, keine Deutsch, non Espanol
+**/
+ {
+ LOG_FUNC
+ ASSERT_DEBUG(iSdpDatabase);
+
+ TBuf8<2> attrId;
+ TBuf8<4> val;
+
+ iRecZero = CSdpServRecord::NewServerSideL(User::Identity());
+
+ // Set Attr 0 (Record handle) to 0
+ attrId.FillZ(2);
+ val.FillZ(4);
+ iRecZero->BuildUintL(attrId)->BuildUintL(val);
+
+ // Set Attr 1 (service class list) to list with UUID = 0x1000
+ attrId[0] = 0x00;
+ attrId[1] = 0x01;
+ iRecZero->BuildUintL(attrId)->BuildDESL()
+ ->StartListL()
+ ->BuildUUIDL(TUUID(TUint16(KServiceDiscoveryServerServiceClassUUID)))
+ ->EndListL();
+
+ // Set Attr 2 (service record state) to 0
+ attrId[0] = 0x00;
+ attrId[1] = 0x02;
+ val.FillZ(4);
+ iRecZero->BuildUintL(attrId)->BuildUintL(val);
+
+ // Set attr 4 (protocol list) to L2CAP, no RFCOMM, no OBEX
+ attrId[0] = 0x00;
+ attrId[1] = 0x04;
+ val.FillZ(4);
+ val[3] = 1;
+ iRecZero->BuildUintL(attrId)->BuildDESL()
+ ->StartListL()
+ ->BuildDESL()
+ ->StartListL()
+ ->BuildUUIDL(TUUID(TUint16(0x0100))) // L2CAP
+ ->EndListL()
+ ->EndListL();
+
+ // Set Attr 5 (browse group list) to list with one UUID
+ // 0x1000 (SDP server class)
+ // this should be updated with other service classes when other services are added.
+ attrId[0] = 0x00;
+ attrId[1] = 5;
+ iRecZero->BuildUintL(attrId)->BuildDESL()
+ ->StartListL()
+ ->BuildUUIDL(TUUID(TUint32(0x1000)))
+ ->EndListL();
+
+ // Set Attr 0x006 (language base)
+ attrId[0] = 0x00;
+ attrId[1] = 0x06;
+ TUint16 lang = 0x656e;
+ TUint16 coding = 0x006a;
+ TUint16 base = 0x0100;
+
+ iRecZero->BuildUintL(attrId)->BuildDESL()
+ ->StartListL()
+ ->BuildUintL(TSdpIntBuf<TUint16>(lang)) // english
+ ->BuildUintL(TSdpIntBuf<TUint16>(coding)) // UTF-8
+ ->BuildUintL(TSdpIntBuf<TUint16>(base)) // language base
+ ->EndListL();
+
+ // Set Attr 0x007 (time to live) to 1200 (0x4B0) seconds (20 minutes)
+ attrId[0] = 0x00;
+ attrId[1] = 0x07;
+ val.FillZ(4);
+ val[2]=4;
+ val[3]=0xb0;
+ iRecZero->BuildUintL(attrId)->BuildUintL(val);
+
+ // Set Attr 0x008 (availability) to 0xff - fully available - not in use
+ attrId[0] = 0x00;
+ attrId[1] = 0x08;
+ TBuf8<1> val4;
+ val4.FillZ(1);
+ val4[0]=0xff;
+ iRecZero->BuildUintL(attrId)->BuildUintL(val4);
+
+ // Set Attr 0x100 (default Name) to string
+ attrId[0] = 0x01;
+ attrId[1] = 0;
+ iRecZero->BuildUintL(attrId)->BuildStringL(_L8("SDP Server"));
+
+ // Set Attr 0x101 (def. description) to string
+ attrId[0] = 0x01;
+ attrId[1] = 1;
+ iRecZero->BuildUintL(attrId)->BuildStringL(_L8("Provides local service information for remote devices."));
+
+ // Set Attr 0x102 (def. provider) to Symbian
+ attrId[0] = 0x01;
+ attrId[1] = 2;
+ iRecZero->BuildUintL(attrId)->BuildStringL(_L8("Symbian OS"));
+
+ // Set Attr 0x200 (version number support) to list with 1.0 and 1.1
+ iRecZero->BuildUintL(TSdpIntBuf<TSdpAttributeID>(0x200))
+ ->BuildDESL()
+ ->StartListL()
+ ->BuildUintL(TSdpIntBuf<TUint16>(0x0100)) // 1.0
+ ->BuildUintL(TSdpIntBuf<TUint16>(0x0101)) // 1.1
+ ->EndListL();
+
+ // Set Attr 0x201 (service database state) to 0
+ attrId[0] = 0x02;
+ attrId[1] = 0x01;
+ val.FillZ(4);
+ iRecZero->BuildUintL(attrId)->BuildUintL(val);
+
+ // Add the record into the database
+ Database()->AddRecord(iRecZero);
+ }
+
+CSession2* CSdpServer::NewSessionL(const TVersion& /*aVersion*/) const
+ {
+ LOG_FUNC
+ User::Leave(KErrNotSupported);
+ return 0;
+ }
+
+CSession2* CSdpServer::NewSessionL(const TVersion& aVersion,const RMessage2& aMessage) const
+ {
+ LOG_FUNC
+ TVersion v(KSdpServerMajorVersionNumber,KSdpServerMinorVersionNumber,KSdpServerBuildVersionNumber);
+ if (!User::QueryVersionSupported(v,aVersion))
+ {
+ User::Leave(KErrNotSupported);
+ }
+ // make new session
+ return new(ELeave) CSdpServSession(aMessage);
+ }
+
+void CSdpServer::AddSession()
+/**
+A new session is being created
+Cancel the shutdown timer if it was running
+**/
+ {
+ LOG_FUNC
+ ++iSessionCount;
+ if (iSessionCount > iMaxSessionCount)
+ {
+ iMaxSessionCount = iSessionCount;
+ }
+ iShutdown.Cancel();
+ }
+
+void CSdpServer::DropSession()
+/**
+A session is being destroyed
+Start the shutdown timer if it is the last session.
+**/
+ {
+ __ASSERT_DEBUG(iSessionCount > 0, PanicServer(ESdpBadState));
+
+ if (--iSessionCount==0)
+ {
+ iShutdown.Start();
+ }
+ }
+
+void CSdpServer::CheckAllowedL(const RMessage2& aMessage, const CSdpServRecord& aRecord)
+ {
+ LOG_FUNC
+ if (!Allowed(aMessage, aRecord))
+ {
+ User::Leave(KErrPermissionDenied);
+ }
+ }
+
+TBool CSdpServer::Allowed(const RMessage2& aMessage, const CSdpServRecord& aRecord)
+ {
+ LOG_FUNC
+ _LIT_SECURITY_POLICY_S0(KSidPolicy, aRecord.ClientUid().iUid);
+
+ //if the secure id of the record matches the secure id of the message sender
+ if (KSidPolicy().CheckPolicy(aMessage))
+ {
+ //allowed
+ return ETrue;
+ }
+
+ //not allowed
+ return EFalse;
+ }
+
+CSdpServRecord* CSdpServer::FindRecordByHandle(const TSdpServRecordHandle aHandle)
+/**
+Returns a pointer to the first record object found with its
+attribute '0' set to the specified handle.
+If no such record object is found, this function return 0 (Null).
+**/
+ {
+ LOG_FUNC
+ for(TServRecordIter recIter(Database()->RecordIter()); recIter; recIter++)
+ {// Iterate thru records in iDatabase
+ if((*recIter).Handle()==aHandle)
+ {
+ //Destructor of a record removes/destroys all its
+ //attributes and deques it from database list.
+ return &(*recIter);
+ }
+ }
+ return 0;
+ }
+
+CSdpAttr* CSdpServer::FindAttributeByID(CSdpServRecord& aRecord, const TSdpAttributeID aAttrID)
+/**
+Returns a pointer to the attribute object found in the
+specified record that has the specified ID.
+If no such attribute object is found, this function return 0 (Null).
+**/
+ {
+ LOG_FUNC
+ for(TServAttrIter attrIter(aRecord.AttributeIter()); attrIter; attrIter++)
+ {// Iterate thru attributes in record
+ if((*attrIter).AttributeID()==aAttrID)
+ {
+ return &(*attrIter);
+ }
+ }
+ return 0;
+ }
+
+/**
+This function must be called whenever a service record is added or removed.
+
+The SDP server 0 service record has a ServerServiceDatabaseState attribute
+which, if present, must be updated when a service record is added or removed.
+*/
+void CSdpServer::DatabaseStateChange()
+ {
+ LOG_FUNC
+ //increment database state attribute in record zero
+ if(iRecZero)
+ {
+ TUint state = iDbState->Uint();
+
+ TPckg<TUint> stateBuf(0);
+ SdpUtil::PutUint(&stateBuf[0], ++state, sizeof(TUint));
+
+ iDbState->SetUintValue(stateBuf);
+
+ // This encodes iDbState into the buffer provided at construction
+ iEncodeBuf.SetLength(0);
+ TRAPD(err, iEncoderVisitor->EncodeAttributeL(*iDbState));
+ // Attribute encoding can only fail if the attribute is of an unknown type
+ // or the supplied buffer is too small. We have set the length of the
+ // buffer to the correct length on creation so that will not fail.
+ // We know iDbState is a CSdpAttrValueUint so cannot fail.
+ __ASSERT_ALWAYS(!err, PanicServer(ESdpAttributeEncodingFailed));
+
+ CSdpAttr* attr = FindAttributeByID(*iRecZero, KSdpAttrIdSdpServerServiceDatabaseState);
+ __ASSERT_ALWAYS(attr->Value().Type() == ETypeEncoded, PanicServer(ESdpStoredAttrValNotEncoded));
+ reinterpret_cast<CSdpAttrValueEncoded&>(attr->Value()).SetEncodedValue(iEncodeBuf);
+
+ iRecZero->RecordStateChange();
+ iUuidManager->NotifySdpRecordChange();
+ }
+ }
+
+CSdpServRecord* CSdpServer::CreateServiceRecordL(const RMessage2& aMessage)
+/**
+Creates a record on the global database. Returns a pointer to the new record.
+**/
+ {
+ LOG_FUNC
+
+ CSdpServRecord* record = CSdpServRecord::NewServerSideL(aMessage.Identity());
+
+ CleanupStack::PushL(record);
+
+ //Build record handle attribute...
+ //..NB if we had created the record using iDatabase.NewRecordL we would not
+ // have to add the handle attribute manually..BUT then to encode the
+ // the handle attribute would have required us to dig it out of the
+ // record first which seems less robust than what is below.
+ // For 1.1 we might create an EncodeRecordL function for a single record
+ // in the same way we now have an EncodeDbL function for the whole database.
+ // This would then allow us to call iDatabase>NewRecordL, followed by
+ // record.EncodeRecordL.
+ CSdpAttr* handleAttr = CSdpAttr::NewL(0x00, record);
+ record->AddAttribute(handleAttr);
+ handleAttr->BuildUintL(TSdpIntBuf<TSdpServRecordHandle>(Database()->NextFreeHandle()));
+ CSdpAttrValue* handleAttrValue = &(handleAttr->Value());
+
+ TUint size = TElementEncoder::EncodedSize(handleAttrValue->Type(),
+ handleAttrValue->DataSize());
+ HBufC8* bufEncodedHandle = HBufC8::NewL(size);
+ CleanupStack::PushL(bufEncodedHandle);
+ TPtr8 ptrEncodedHandle = bufEncodedHandle->Des();
+
+ TElementEncoder ee(ptrEncodedHandle);
+ CAttrEncoderVisitor::EncodeAttributeL(ee, *handleAttrValue);
+ handleAttr->BuildEncodedL(ptrEncodedHandle);
+
+ CleanupStack::PopAndDestroy(); //bufEncodedHandle
+
+ //Write record handle back to EPOC client (i.e. user)
+ TSdpServRecordHandlePckgBuf pckg(record->Handle());
+ pckg() = record->Handle();
+ aMessage.WriteL(1, pckg); //ss//
+
+ //Build service class attribute...
+ TInt bufServiceClassLen = aMessage.GetDesLengthL(0); //ss//
+ HBufC8* bufServiceClass = HBufC8::NewLC(bufServiceClassLen);
+ TPtr8 ptrServiceClass = bufServiceClass->Des();
+ aMessage.ReadL(0, ptrServiceClass); //get message data into buf (via ptr) //ss//
+
+ TBuf8<2> attrId;
+ attrId.FillZ(2); // Order of following lines important to value in attrId!!!
+ attrId[0] = 0x00;
+ attrId[1] = 0x01;
+
+ CSdpAttr* attr = CSdpAttr::NewL(0x0001, record);
+ record->AddAttribute(attr);
+ attr->BuildEncodedL(ptrServiceClass);
+
+ //Add record, now with handle and service class attributes, to database
+ Database()->AddRecord(record);
+ CleanupStack::PopAndDestroy(); //bufServiceClass
+ CleanupStack::Pop(); //record,
+
+ DatabaseStateChange();
+
+ //Return a pointer to the new record
+ return record;
+ }
+
+CSdpServRecord* CSdpServer::FindAndCheckServiceRecordForDeletion(const RMessage2& aMessage)
+/**
+Checks it's ok to delete a record on the global database using handle sent in message slot 0.
+Returns the pointer of the record that is to be deleted.
+**/
+ {
+ LOG_FUNC
+
+ TSdpServRecordHandlePckgBuf pckg;
+ TRAPD(err, aMessage.ReadL(0, pckg)); //get message data into buf (via ptr)
+ if(err)
+ {
+ // Client has sent us a dodgy descriptor, punish them!
+ PanicClient(aMessage, ESdpBadDescriptor);
+ return NULL;
+ }
+
+ //the service record handle should be non-zero
+ __ASSERT_DEBUG(pckg() != 0, Panic(ESdpServerDeleteServiceRecordHandleZero));
+
+ CSdpServRecord* record = FindRecordByHandle(pckg());
+ if(!record)
+ {
+ PanicClient(aMessage, ESdpNonExistantRecordHandle);
+ return NULL;
+ }
+
+ TRAP(err, CheckAllowedL(aMessage, *record));
+ if(err)
+ {
+ // Client has been naughty, trying to delete things they shouldn't!
+ PanicClient(aMessage, ESdpBadRequest);
+ return NULL;
+ }
+
+ //Return a pointer to the record to be deleted
+ return record;
+ }
+
+void CSdpServer::UpdateAttributeL(const RMessage2& aMessage)
+/**
+Updates/Creates an attribute in a record on the global database using the packaged
+record handle, the packaged attribute id, and the encoded attribute value
+sent in message slots 0, 1, and 2.
+**/
+ {
+ LOG_FUNC
+
+ TSdpAttributeIDPckgBuf idPckg;
+ TSdpServRecordHandlePckgBuf handlePckg;
+
+ aMessage.ReadL(0, handlePckg); //get handle into handle package
+
+ //the service record handle should be non-zero
+ __ASSERT_DEBUG(handlePckg() != 0, Panic(ESdpServerUpdateAttributeRecordHandleZero));
+
+ aMessage.ReadL(1, idPckg); //get id into id package
+ TInt bufLen = aMessage.GetDesLengthL(2);
+ HBufC8* buf = HBufC8::NewLC(bufLen);
+ TPtr8 ptr = buf->Des();
+ aMessage.ReadL(2, ptr); //get message data into buf (via ptr)
+
+ CSdpServRecord* record = FindRecordByHandle(handlePckg());
+ if(!record)
+ {
+ PanicClient(aMessage, ESdpNonExistantRecordHandle);
+ return;
+ }
+
+ CheckAllowedL(aMessage, *record);
+
+ UpdateOrCreateAtributeL(*record, idPckg(), ptr);
+
+ iUuidManager->NotifySdpRecordChange();
+
+ CleanupStack::PopAndDestroy(); //buf
+ }
+
+void CSdpServer::UpdateOrCreateAtributeL(CSdpServRecord& aRecord, TSdpAttributeID aAttrId, const TDesC8& aBuf)
+ {
+ LOG_FUNC
+ CSdpAttr* attr = FindAttributeByID(aRecord, aAttrId);
+ if(attr)
+ {
+ //Deletes old attr value, and replaces with encoded value found in 'ptr'.
+ attr->BuildEncodedL(aBuf);
+ }
+ else
+ {
+ TPckg<TUint16> idBuf(0);
+ BigEndian::Put16(&idBuf[0], aAttrId);
+ static_cast<CSdpAttr*>(aRecord.BuildUintL(idBuf))->BuildEncodedL(aBuf);
+ }
+
+ aRecord.RecordStateChange();
+ }
+
+void CSdpServer::DeleteAttribute(const RMessage2& aMessage)
+/**
+Deletes an attribute on the global database using the packaged record and the packaged
+attribute id found in record handle and attribute id sent in message slots 0 and 1.
+**/
+ {
+ LOG_FUNC
+
+ TSdpAttributeIDPckgBuf idPckg;
+ TSdpServRecordHandlePckgBuf handlePckg;
+
+ TRAPD(err, aMessage.ReadL(0, handlePckg)); //get handle into handle package
+ if(err)
+ {
+ // Client has sent us a dodgy descriptor, punish them!
+ PanicClient(aMessage, ESdpBadDescriptor);
+ return;
+ }
+
+ //the service record handle should be non-zero
+ __ASSERT_DEBUG(handlePckg() != 0, Panic(ESdpServerDeleteAttributeRecordHandleZero));
+
+ TRAP(err, aMessage.ReadL(1, idPckg)); //get id into id package
+ if(err)
+ {
+ // Client has sent us a dodgy descriptor, punish them!
+ PanicClient(aMessage, ESdpBadDescriptor);
+ return;
+ }
+
+ CSdpServRecord* record = FindRecordByHandle(handlePckg());
+ if(!record)
+ {
+ PanicClient(aMessage, ESdpNonExistantRecordHandle);
+ return;
+ }
+
+ TRAP(err, CheckAllowedL(aMessage, *record));
+ if(err)
+ {
+ // Client has been naughty, trying to delete things they shouldn't!
+ PanicClient(aMessage, ESdpBadRequest);
+ return;
+ }
+
+ CSdpAttr* attr = FindAttributeByID(*record, idPckg());
+ if(!attr)
+ {
+ return;
+ }
+
+ delete attr;
+
+ record->RecordStateChange();
+
+ return;
+ }
+
+void CSdpServer::DeleteServiceRecord(CSdpServRecord* aServiceRecord)
+/**
+Deletes a record on the global database using a pointer to the record. This
+is used to delete records added to a session when the session is closed if
+the records have not been deleted by the client.
+**/
+ {
+ LOG_FUNC
+ if (aServiceRecord)
+ {
+ //Destructor of a record removes/destroys all its
+ //attributes and deques it from database list.
+ delete aServiceRecord;
+ DatabaseStateChange();
+ }
+ }
+
+TInt CSdpServer::RunError(TInt aError)
+/**
+ Handle an error from ServiceL()
+ A bad descriptor error implies a badly programmed client, so panic it;
+ otherwise report the error to the client
+**/
+ {
+ LOG_FUNC
+ LOG1(_L("s\tCSdpServer::RunError(): %d"), aError);
+ if (aError==KErrBadDescriptor)
+ {
+ PanicClient(Message(),ESdpBadDescriptor);
+ }
+ else
+ {
+ Message().Complete(aError);
+ }
+ ReStart();
+ return KErrNone; // handled the error fully
+ }
+
+void PanicClient(const RMessage2& aMessage, TSdpClientPanic aPanic)
+/**
+ Panic the client and complete the message.
+ RMessage2::Panic() also completes the message. This is:
+ (a) important for efficient cleanup within the kernel
+ (b) a problem if the message is completed a second time
+**/
+ {
+ LOG_STATIC_FUNC
+ LOG1(_L("s\tPanicClient: Reason = %d"), aPanic);
+ aMessage.Panic(KSdpClientPanic,aPanic);
+ }
+
+void PanicServer(TSdpServerPanic aPanic)
+/**
+Panic our own thread
+**/
+ {
+ LOG_STATIC_FUNC
+ LOG1(_L("s\tPanicServer: Reason = %d"), aPanic);
+ User::Panic(KSdpServerPanic, aPanic);
+ }
+
+class CAttrPrintVisitor : public CBase, public MAttributeVisitor
+ {
+public:
+ CAttrPrintVisitor(){}
+#ifdef _DEBUG
+ void VisitAttributeL(CSdpAttr& aAttribute)
+#else
+ void VisitAttributeL(CSdpAttr& /*aAttribute*/)
+#endif
+ {
+ Indent();
+ LOG1(_L("Attribute ID: 0x%x"), aAttribute.AttributeID());
+ }
+ void VisitAttributeValueL(CSdpAttrValue & aValue, TSdpElementType aType)
+ {
+ TBuf16<64> iString;
+ switch (aType)
+ {
+ case ETypeString:
+ iString.Copy(aValue.Des());
+ LOG1(_L("\"%S\""),&iString);
+ break;
+ case ETypeDES:
+ LOG(_L(" DES"));
+ break;
+ case ETypeUint:
+ LOG1(_L(" UInt:0x%x"), aValue.Uint());
+ break;
+ case ETypeUUID:
+ LOG(_L(" UUID:0x"));
+ HexDes(aValue.UUID().ShortestForm());
+ break;
+ case ETypeEncoded:
+ HexDes(aValue.Des()); // simplest
+ break;
+ default:
+ LOG1(_L("type %d"), aType);
+ }
+ }
+ void StartListL(CSdpAttrValueList &/*aList*/)
+ {
+ ++iIndent;
+ Indent();
+ LOG(_L("{"));
+ }
+ void EndListL()
+ {
+ if(iIndent<=0)
+ {
+ LOG(_L("ERROR! Unmatched EndList!"));
+ __DEBUGGER();
+ }
+ Indent();
+ LOG(_L("}"));
+ --iIndent;
+ }
+private:
+#ifdef _DEBUG
+ void Indent() {for(TInt i=0; i<iIndent;++i)
+ LOG(_L(" "));}
+
+ void HexDes(const TDesC8& aDes)
+ {
+ for (TInt i = 0; i < aDes.Length(); ++i)
+ LOG1(_L("%02x"), aDes[i]);
+ };
+#else
+ void Indent() {}
+ void HexDes(const TDesC8&) {}
+#endif
+ TInt iIndent;
+ };
+
+void FlogDb(CSdpDatabase& aDb)
+ {
+ LOG_STATIC_FUNC
+ LOG(_L("Printing Database..."));
+
+ for(TServRecordIter recIter(aDb.RecordIter()); recIter; recIter++)
+ {// Iterate thru records in Db
+ LOG1(_L("...Printing Record 0x%x"), (*recIter).Handle());
+ for(TServAttrIter attrIter((*recIter).AttributeIter()); attrIter; attrIter++)
+ {// Iterate thru attributes in record
+ CAttrPrintVisitor* theVisitor = new CAttrPrintVisitor();
+ if (theVisitor)
+ {
+ TRAP_IGNORE((*attrIter).AcceptVisitorL(*theVisitor));
+ delete theVisitor;
+ }
+ }
+ }
+ LOG(_L("End Printing Database..."));
+ }
+
+
+
+static void RunServerL(/*TSdpServerStart& aStart*/)
+
+/**
+Perform all server initialisation, in particular creation of the
+scheduler and server and then run the scheduler
+**/
+ {
+ LOG_STATIC_FUNC
+
+ CActiveScheduler *scheduler = new (ELeave) CActiveScheduler;
+ CleanupStack::PushL(scheduler); //1st Push
+ CActiveScheduler::Install(scheduler);
+
+ // create the server (leave it on the cleanup stack)
+ CSdpServer::NewLC();//2nd Push
+ //
+
+#ifdef __SDPSERVER_NO_PROCESSES__
+ RThread::Rendezvous(KErrNone); // this causes the client's Rendezvous to complete
+#else
+ // naming the server thread after the server helps to debug panics
+ // ignore the error returned here - it's not critcal
+ User::RenameThread(KSdpServerName);
+
+ // Initialisation complete, now signal the client
+ RProcess::Rendezvous(KErrNone); // this causes the client's Rendezvous to complete
+#endif
+
+ //
+ // Ready to run
+ CActiveScheduler::Start();
+ //
+ // Cleanup the server and scheduler
+ CleanupStack::PopAndDestroy(2);
+ }
+
+
+TInt E32Main()
+/**
+Server process entry-point
+Recover the startup parameters and run the server
+**/
+ {
+
+ __UHEAP_MARK;
+#ifdef TEST_OOM //define TEST_OOM in preprocessor definitions in project settings
+ __UHEAP_SETFAIL(RHeap::ERandom, 100); //Qualified - this results in a false positive error from leavescan (... may employ a macro), because the macro name ends with an 'L'.
+#endif
+ CTrapCleanup* cleanup=CTrapCleanup::New();
+ TInt r=KErrNoMemory;
+ if (cleanup)
+ {
+ TRAP(r,RunServerL());
+ delete cleanup;
+ }
+ //
+ __UHEAP_MARKEND;
+
+ return r;
+ }
+
+
+//=====================================================================
+//CSdpServSession
+//=====================================================================
+
+CSdpServer& CSdpServSession::Server()
+ {
+ LOG_FUNC
+ return *static_cast<CSdpServer*>(const_cast<CServer2*>(CSession2::Server()));
+ }
+
+// constructor - must pass client to CSession2
+CSdpServSession::CSdpServSession(const RMessage2& /*aMessage*/)
+ {
+ LOG_FUNC
+ }
+
+void CSdpServSession::CreateL(const CServer2& /*aServer*/)
+ {
+ LOG_FUNC
+ User::Leave(KErrNotSupported); //FIXME - PRE SECURE_API - NEEDED TO AVOID COMPILER WARNING
+ }
+
+void CSdpServSession::CreateL()
+/**
+2nd phase construct for sessions - called by the CServer2 framework
+**/
+ {
+ LOG_FUNC
+ //CSession2::CreateL(); // private and does nowt so removed
+ //Add session to server first. If anything leaves, it will be removed by the destructor
+ Server().AddSession();
+ ConstructL();
+ }
+
+void CSdpServSession::ConstructL()
+ {
+ LOG_FUNC
+ // try to reopen the acceptor
+ Server().Listener().TryRestartL();
+ }
+
+CSdpServSession::~CSdpServSession()
+ {
+ LOG_FUNC
+ Server().DropSession();
+
+ //Cleanup any records that have been added during this session but not removed
+ RemoveSessionRecords();
+ }
+
+void CSdpServSession::RemoveSessionRecords()
+ {
+ LOG_FUNC
+ for (TInt index = iSessionRecords.Count()-1; index >= 0; index--)
+ {
+ Server().DeleteServiceRecord(iSessionRecords[index]);
+ }
+ iSessionRecords.Close();
+ }
+
+void CSdpServSession::ServiceL(const RMessage2& aMessage)
+/**
+ Handle a client request.
+**/
+ {
+ LOG_FUNC
+ switch(aMessage.Function())
+ {
+ case ESdpCreateAgentSubSession:
+ NewSubSessionL(ESdpAgent, aMessage);
+ break;
+ case ESdpCreateDatabaseSubSession:
+ NewSubSessionL(ESdpDatabase, aMessage);
+ break;
+ case ESdpResourceCountMarkStart:
+ ResourceCountMarkStart();
+ aMessage.Complete(KErrNone);
+ break;
+ case ESdpResourceCountMarkEnd:
+ ResourceCountMarkEnd(aMessage); //ss//
+ aMessage.Complete(KErrNone);
+ break;
+ case ESdpResourceCount:
+ NumResourcesL(aMessage);
+ break;
+ case ESdpCloseSubSession:
+ CloseSubSession(aMessage);
+ break;
+
+ case ESdpDatabaseCreateServiceRecord:
+ {
+ if (aMessage.Int3() != KSdpDatabaseSubSessionHandle)
+ {
+ PanicClient(aMessage, ESdpBadSubSessionHandle);
+ return;
+ }
+
+ CSdpServRecord* newRecord = NULL;
+ TRAPD(err, newRecord = Server().CreateServiceRecordL(aMessage));
+ // Inspect iHandle to see if we panicked the client and already completed
+ if (aMessage.Handle())
+ {
+ if (newRecord)
+ {
+ // Update our list of records for this session. Ignore error as
+ // we wouldn't want to remove the record if an error did occur,
+ // which is very unlikely as typically only 1 or 2 records are
+ // added per session.
+ iSessionRecords.Append(newRecord);
+ }
+ aMessage.Complete(err);
+ }
+ return;
+ }
+ case ESdpDatabaseDeleteServiceRecord:
+ {
+ if (aMessage.Int3() != KSdpDatabaseSubSessionHandle)
+ {
+ PanicClient(aMessage, ESdpBadSubSessionHandle);
+ return;
+ }
+
+ CSdpServRecord* recordToBeDeleted = Server().FindAndCheckServiceRecordForDeletion(aMessage);
+
+ // If recordToBeDeleted returned is NULL, PanicClient will have been called so aMessage.Handle()
+ // is zero and we've already completed - see if this is not the case
+ if (aMessage.Handle())
+ {
+ TInt index = iSessionRecords.Find(recordToBeDeleted); // Find the index of the record before deleting the record
+ // Ignore if the record is not found as it may not have been added
+ // in the first place.
+ if (index >= 0)
+ {
+ iSessionRecords.Remove(index);
+ }
+ Server().DeleteServiceRecord(recordToBeDeleted);
+ aMessage.Complete(KErrNone);
+ }
+ return;
+ }
+ case ESdpDatabaseUpdateAttribute:
+ {
+ if (aMessage.Int3() != KSdpDatabaseSubSessionHandle)
+ {
+ PanicClient(aMessage, ESdpBadSubSessionHandle);
+ return;
+ }
+
+ TRAPD(err, Server().UpdateAttributeL(aMessage));
+ // Inspect iHandle to see if we panicked the client and already completed
+ if (aMessage.Handle())
+ {
+ aMessage.Complete(err);
+ }
+ return;
+ }
+ case ESdpDatabaseDeleteAttribute:
+ {
+ if (aMessage.Int3() != KSdpDatabaseSubSessionHandle)
+ {
+ PanicClient(aMessage, ESdpBadSubSessionHandle);
+ return;
+ }
+
+ Server().DeleteAttribute(aMessage);
+ // Inspect iHandle to see if we panicked the client and already completed
+ if (aMessage.Handle())
+ {
+ aMessage.Complete(KErrNone);
+ }
+ return;
+ }
+ case ESdpServerDbgMarkHeap:
+ {
+#ifdef _DEBUG
+ LOG(_L("\tmark sdp server heap"));
+ __UHEAP_MARK;
+#endif // _DEBUG
+ aMessage.Complete(KErrNone);
+ break;
+ }
+ case ESdpServerDbgCheckHeap:
+ {
+#ifdef _DEBUG
+ LOG1(_L("\tcheck sdp server heap (expecting %d cells)"), aMessage.Int0());
+ __UHEAP_CHECK(aMessage.Int0());
+#endif // _DEBUG
+ aMessage.Complete(KErrNone);
+ break;
+ }
+ case ESdpServerDbgMarkEnd:
+ {
+#ifdef _DEBUG
+ LOG1(_L("\tmark end sdp server heap (expecting %d cells)"), aMessage.Int0());
+ __UHEAP_MARKENDC(aMessage.Int0());
+#endif // _DEBUG
+ aMessage.Complete(KErrNone);
+ break;
+ }
+ case ESdpServerDbgFailNext:
+ {
+#ifdef _DEBUG
+ LOG1(_L("\tfail next sdp server alloc (simulating failure after %d allocation(s))"), aMessage.Int0());
+ if ( aMessage.Int0() == 0 )
+ {
+ __UHEAP_RESET;
+ }
+ else
+ {
+ __UHEAP_FAILNEXT(aMessage.Int0());
+ }
+#endif // _DEBUG
+ aMessage.Complete(KErrNone);
+ break;
+ }
+ default:
+ //not handled here so must be for subsession
+ PanicClient(aMessage, ESdpBadRequest);
+ }
+ }
+
+void CSdpServSession::NewSubSessionL(TSubSessionType aType, const RMessage2& aMessage)
+/**
+ Increment our subsession count and send the SDP subsession handle
+ back to the client.
+ If we succeed, we complete the message. If we fail, we leave
+ and the server sends back the error for us.
+**/
+ {
+ LOG_FUNC
+ if (aType!=ESdpDatabase)
+ {
+ User::Leave(KErrNotSupported);
+ }
+
+ //Write handle to client
+ TPckg<TInt> pckg(KSdpDatabaseSubSessionHandle);
+ TRAPD(err, aMessage.WriteL(3, pckg)); //ss//
+ if(err)
+ {
+ User::Leave(err);
+ }
+ iSubSessionCount++;
+ aMessage.Complete(KErrNone);
+ }
+
+void CSdpServSession::DeleteSubsession(TUint aHandle, const RMessage2& aMessage)
+ {
+ LOG_FUNC
+ //panic client if bad handle
+ if ((aHandle != KSdpDatabaseSubSessionHandle) || (iSubSessionCount == 0))
+ {
+ PanicClient(aMessage, ESdpBadSubSessionRemove);
+ return;
+ }
+ iSubSessionCount--;
+ aMessage.Complete(KErrNone);
+ }
+
+void CSdpServSession::CloseSubSession(const RMessage2& aMessage)
+ {
+ LOG_FUNC
+
+ DeleteSubsession((aMessage).Int3(), aMessage); //calls aMessage.Complete(KErrNone)
+ }
+
+void CSdpServSession::NumResourcesL(const RMessage2& aMessage)
+ {
+ LOG_FUNC
+ TPckgBuf<TInt> pckg(CountResources());
+ aMessage.WriteL(0, pckg); //ss//
+ aMessage.Complete(KErrNone);
+ }
+
+TInt CSdpServSession::CountResources()
+ {
+ LOG_FUNC
+ return iSubSessionCount;
+ }
+
+
+//**********************************
+// CEirPublisherSdpUuidBase
+//**********************************
+/**
+CEirPublisherSdpUuidBase provides base class for Sdp Uuid Eir Publisher.
+**/
+
+CEirPublisherSdpUuidBase::~CEirPublisherSdpUuidBase()
+ {
+ delete iPublisher;
+ }
+
+CEirPublisherSdpUuidBase::CEirPublisherSdpUuidBase(CSdpUuidManager& aSdpUuidManager, TEirTag aTag)
+: iParent(aSdpUuidManager), iTag(aTag)
+ {
+ }
+
+void CEirPublisherSdpUuidBase::ConstructL()
+ {
+ iPublisher = CEirPublisher::NewL(iTag, *this);
+ }
+
+void CEirPublisherSdpUuidBase::UpdateUuids(TInt aLength)
+ {
+ iPublisher->PublishData(aLength);
+ }
+
+//**********************************
+// CEirPublisherSdpUuid16
+//**********************************
+/**
+Provides functionality to publish 16 bit UUIDs to EIR.
+**/
+CEirPublisherSdpUuid16* CEirPublisherSdpUuid16::NewL(CSdpUuidManager& aSdpUuidManager)
+ {
+ CEirPublisherSdpUuid16* self = new (ELeave) CEirPublisherSdpUuid16(aSdpUuidManager);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ CleanupStack::Pop();
+ return self;
+ }
+
+CEirPublisherSdpUuid16::CEirPublisherSdpUuid16(CSdpUuidManager& aSdpUuidManager)
+: CEirPublisherSdpUuidBase(aSdpUuidManager, EEirTagSdpUuid16)
+ {
+ }
+
+CEirPublisherSdpUuid16::~CEirPublisherSdpUuid16()
+ {
+ delete iExtracted;
+ }
+
+// From MEirPublisherNotifier
+void CEirPublisherSdpUuid16::MepnSpaceAvailable(TUint aBytesAvailable)
+ {
+ // Ensure previous memory is freed
+ delete iExtracted, iExtracted = NULL;
+ TBool partial = EFalse;
+
+ if (aBytesAvailable > 0)
+ {
+ iExtracted = iParent.GetAll16BitUUIDs(aBytesAvailable, partial);
+ if(!iExtracted)
+ {
+ // OOM probably
+ iPublisher->SetData(KNullDesC8, EEirDataPartial);
+ }
+ else if(partial)
+ {
+ iPublisher->SetData(*iExtracted, EEirDataPartial);
+ }
+ else
+ {
+ iPublisher->SetData(*iExtracted, EEirDataComplete);
+ }
+ }
+ }
+
+void CEirPublisherSdpUuid16::MepnSetDataError(TInt /*aResult*/)
+ {
+ delete iExtracted;
+ iExtracted = NULL;
+ }
+
+
+//**********************************
+// CEirPublisherSdpUuid128
+//**********************************
+/**
+Provides functionality to publish 128 bit UUIDs to EIR.
+**/
+CEirPublisherSdpUuid128* CEirPublisherSdpUuid128::NewL(CSdpUuidManager& aSdpUuidManager)
+ {
+ CEirPublisherSdpUuid128* self = new (ELeave) CEirPublisherSdpUuid128(aSdpUuidManager);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ CleanupStack::Pop();
+ return self;
+ }
+
+CEirPublisherSdpUuid128::CEirPublisherSdpUuid128(CSdpUuidManager& aSdpUuidManager)
+: CEirPublisherSdpUuidBase(aSdpUuidManager, EEirTagSdpUuid128)
+ {
+ }
+
+CEirPublisherSdpUuid128::~CEirPublisherSdpUuid128()
+ {
+ delete iExtracted;
+ }
+
+// From MEirPublisherNotifier
+void CEirPublisherSdpUuid128::MepnSpaceAvailable(TUint aBytesAvailable)
+ {
+ // Ensure previous memory is freed
+ delete iExtracted, iExtracted = NULL;
+ TBool partial = EFalse;
+
+ if (aBytesAvailable > 0)
+ {
+ iExtracted = iParent.GetAll128BitUUIDs(aBytesAvailable, partial);
+ if(!iExtracted)
+ {
+ // OOM probably
+ iPublisher->SetData(KNullDesC8, EEirDataPartial);
+ }
+ else if(partial)
+ {
+ iPublisher->SetData(*iExtracted, EEirDataPartial);
+ }
+ else
+ {
+ iPublisher->SetData(*iExtracted, EEirDataComplete);
+ }
+ }
+ }
+
+void CEirPublisherSdpUuid128::MepnSetDataError(TInt /*aResult*/)
+ {
+ delete iExtracted;
+ iExtracted = NULL;
+ }
+
+
+CSdpUuidManager* CSdpUuidManager::NewL(CSdpDatabase& aSdpDatabase)
+ {
+ CSdpUuidManager* self = new (ELeave) CSdpUuidManager(aSdpDatabase);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ CleanupStack::Pop();
+ return self;
+ }
+
+CSdpUuidManager::~CSdpUuidManager()
+ {
+ i16BitUUIDs.Close();
+ i128BitUUIDs.Close();
+
+ delete iEirPublisherSdpUuid16;
+ delete iEirPublisherSdpUuid128;
+ delete iUuidVisitor;
+ }
+
+void CSdpUuidManager::NotifySdpRecordChange()
+ {
+ TServRecordIter recIter(iSdpDatabase.RecordIter());
+ CSdpServRecord* rec = NULL;
+ CSdpAttr* attr = NULL;
+ CSdpAttrValue* serviceClassValue = NULL;
+ CSdpAttrValue* browseGroupValue = NULL;
+
+ ResetUuids();
+ while((rec = recIter++) != NULL)
+ {// Iterate thru records in Db
+ TServAttrIter attrIter(rec->AttributeIter());
+ while((attr = attrIter++) != NULL)
+ {
+ if(attr->AttributeID()==KSdpAttrIdServiceClassIDList)
+ {
+ // Find the value of ServiceClassIDList attribute
+ serviceClassValue = &(attr->Value());
+ }
+ if(attr->AttributeID()==KSdpAttrIdBrowseGroupList)
+ {
+ // Find the value of BrowseGroupList attribute
+ browseGroupValue = &(attr->Value());
+ }
+ }
+ if(serviceClassValue && browseGroupValue)
+ {
+ // Both ServiceClassIDList and BrowseGroupList have value
+ iUuidVisitor->GetUuids().Reset();
+ TRAPD(err, browseGroupValue->AcceptVisitorL(*iUuidVisitor));
+ if(err == KErrNone)
+ {
+ TUUID publicBrowseGroupUUID = TUUID(static_cast<TUint32>(KPublicBrowseGroupUUID));
+ if(iUuidVisitor->GetUuids().IsPresent(publicBrowseGroupUUID))
+ {
+ iUuidVisitor->GetUuids().Reset();
+ TRAPD(err, serviceClassValue->AcceptVisitorL(*iUuidVisitor));
+ if(err == KErrNone && iUuidVisitor->GetUuids().Count() > 0)
+ {
+ // In eir, we use the 1st uuid on the list, as it is the most specific one
+ AddUuid(iUuidVisitor->GetUuids()[0]);
+ }
+ }
+ }
+ }
+ // Reset the pointers for these two values
+ serviceClassValue = NULL;
+ browseGroupValue = NULL;
+ }
+ // Only publish UUIDs in public browse group
+ // 16 bit UUIDs
+ TUint8 count = i16BitUUIDs.Count();
+ // Skip the 1st 16 bit uuid record, it's a SDP service UUID
+ if(count > 0)
+ {
+ // UpdateUuids expects the length in bytes, so multiply number of uuids by 2
+ iEirPublisherSdpUuid16->UpdateUuids(count<<1);
+ }
+
+ // 128 bit UUIDs
+ count = i128BitUUIDs.Count();
+ if(count > 0)
+ {
+ // UpdateUuids expects the length in bytes, so multiply number of uuids by 16
+ iEirPublisherSdpUuid128->UpdateUuids(count<<4);
+ }
+ }
+
+void CSdpUuidManager::AddUuid(TUUID aUuid)
+ {
+ TUint8 count = aUuid.MinimumSize();
+ if(count == KSizeOf16BitUUID)
+ {
+ // 16 bit UUID
+ i16BitUUIDs.Add(aUuid);
+ }
+ else if(count == KSizeOf128BitUUID)
+ {
+ // 128 bit UUID
+ i128BitUUIDs.Add(aUuid);
+ }
+ else
+ {
+ // 32 bit UUIDs are not supported
+ }
+ }
+
+void CSdpUuidManager::ResetUuids()
+ {
+ i16BitUUIDs.Reset();
+ i128BitUUIDs.Reset();
+ }
+
+HBufC8* CSdpUuidManager::GetAll16BitUUIDs(TInt aBytesAvailable, TBool& aPartial)
+ {
+ // Flatten the UUIDs into a little-endian buffer
+ HBufC8* extracted = HBufC8::New(aBytesAvailable);
+ if(extracted)
+ {
+ TPtr8 ptr = extracted->Des();
+ TInt i = 0;
+ TInt count = i16BitUUIDs.Count();
+ // Iterate whilst not exhausted and there's space for a UUID
+ while (i < count && ptr.Length() + KSizeOf16BitUUID <= aBytesAvailable)
+ {
+ // convert to 16 bit Little Endian for EIR
+ TPtrC8 p = i16BitUUIDs[i].ShortestForm();
+ ptr.Append(p[1]);
+ ptr.Append(p[0]);
+ i++;
+ }
+ __ASSERT_DEBUG(extracted->Length() <= aBytesAvailable, Panic(ESdpServerUuidFlattenBroken));
+ // Let the caller know if there wasn't enough space for all UUIDs
+ aPartial = EFalse;
+ if (i < count)
+ {
+ aPartial = ETrue;
+ }
+ }
+ return extracted;
+ }
+
+HBufC8* CSdpUuidManager::GetAll128BitUUIDs(TInt aBytesAvailable, TBool& aPartial)
+ {
+ // Flatten the UUIDs into a little-endian buffer
+ HBufC8* extracted = HBufC8::New(aBytesAvailable);
+ if(extracted)
+ {
+ TPtr8 ptr = extracted->Des();
+ TInt i = 0;
+ TInt count = i128BitUUIDs.Count();
+
+ // Iterate whilst not exhausted and there's space for a UUID
+ while (i < count && ptr.Length() + KSizeOf128BitUUID <= aBytesAvailable)
+ {
+ // convert to 128 bits Little Endian for EIR
+ for (TUint j = 1; j<= KSdpUUIDMaxLength ;j++)
+ {
+ ptr.Append(i128BitUUIDs[i][KSdpUUIDMaxLength - j]);
+ }
+ i++;
+ }
+ __ASSERT_DEBUG(extracted->Length() <= aBytesAvailable, Panic(ESdpServerUuidFlattenBroken));
+ // Let the caller know if there wasn't enough space for all UUIDs
+ aPartial = EFalse;
+ if (i < count)
+ {
+ aPartial = ETrue;
+ }
+ }
+ return extracted;
+ }
+
+CSdpUuidManager::CSdpUuidManager(CSdpDatabase& aSdpDatabase)
+: iSdpDatabase(aSdpDatabase)
+ {
+ }
+
+void CSdpUuidManager::ConstructL()
+ {
+ iUuidVisitor = CAttrUuidVisitor::NewL();
+ iEirPublisherSdpUuid16 = CEirPublisherSdpUuid16::NewL(*this);
+ iEirPublisherSdpUuid128 = CEirPublisherSdpUuid128::NewL(*this);
+ }
+