bluetooth/btexample/example/btsocket/src/cbtclient.cpp
changeset 0 29b1cd4cb562
equal deleted inserted replaced
-1:000000000000 0:29b1cd4cb562
       
     1 // Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 // Name     : CBtClient.cpp
       
    15 // Part of  : ex_btsocket
       
    16 // Created  : 17/11/2004 by Shane Kearns
       
    17 // Client "smart connector" class
       
    18 // Version  :
       
    19 // 
       
    20 //
       
    21 
       
    22 #include <e32std.h>
       
    23 #include <bt_sock.h>
       
    24 #include <btsdp.h>
       
    25 #include <btextnotifiers.h>
       
    26 #include "cbtclient.h"
       
    27 
       
    28 /**
       
    29 Standard Symbian style 2 phase construction
       
    30 @param aServiceUUID a universally unique identifier for this application.
       
    31 @param aSocketServer a constructed socket server session owned by the application
       
    32 @param aOwner the owning class which should receive notification of new connections
       
    33 @param aSecurityRequirements optional security settings to use on the socket instead of defaults.  The default is Authorisation only ("Accept connection from xxx" dialog but no pairing)
       
    34 */
       
    35 EXPORT_C CBtClient* CBtClient::NewL(const TUUID& aServiceUUID, RSocketServ& aSocketServer, MConnectionObserver& aOwner, TBTServiceSecurity* aSecurityRequirements)
       
    36 	{
       
    37 	LOG_FN_TRACE((_L("+CBtClient::NewL")));
       
    38 	CBtClient* self = new(ELeave) CBtClient(aServiceUUID, aSocketServer, aOwner);
       
    39 	CleanupStack::PushL(self);
       
    40 	self->ConstructL(aSecurityRequirements);
       
    41 	CleanupStack::Pop(self);
       
    42 	LOG_FN_TRACE((_L("-CBtClient::NewL")));
       
    43 	return self;
       
    44 	}
       
    45 
       
    46 /**
       
    47 First phase constructor
       
    48 initialise member data
       
    49 */
       
    50 CBtClient::CBtClient(const TUUID& aServiceUUID, RSocketServ& aSocketServer, MConnectionObserver& aOwner) :
       
    51 	CActive(CActive::EPriorityStandard),
       
    52 	iSocketServer(aSocketServer),
       
    53 	iServiceUUID(aServiceUUID),
       
    54 	iOwner(aOwner)
       
    55 	{
       
    56 	}
       
    57 
       
    58 /**
       
    59 Second phase constructor
       
    60 Leaving initialisation
       
    61 */
       
    62 void CBtClient::ConstructL(TBTServiceSecurity* aSecurityRequirements)
       
    63 	{
       
    64 	LOG_FN_TRACE((_L("+CBtClient::ConstructL")));
       
    65 	CActiveScheduler::Add(this);
       
    66 	if(aSecurityRequirements)
       
    67 		{
       
    68 		iSecurityRequirements = *aSecurityRequirements;
       
    69 		}
       
    70 	else
       
    71 		{
       
    72 		//default security settings - authorisation only
       
    73 		iSecurityRequirements.SetDenied(EFalse);
       
    74 		iSecurityRequirements.SetAuthorisation(ETrue);
       
    75 		iSecurityRequirements.SetAuthentication(EMitmNotRequired);
       
    76 		iSecurityRequirements.SetEncryption(EFalse);
       
    77 		}
       
    78 	LOG_FN_TRACE((_L("-CBtClient::ConstructL")));
       
    79 	}
       
    80 
       
    81 /**
       
    82 Destructor
       
    83 free all resources
       
    84 */
       
    85 EXPORT_C CBtClient::~CBtClient()
       
    86 	{
       
    87 	LOG_FN_TRACE((_L("+CBtClient::~CBtClient")));
       
    88 	Deque();
       
    89 	delete iConnectionSocket;
       
    90 	delete iSdpAgent;
       
    91 	LOG_FN_TRACE((_L("-CBtClient::~CBtClient")));
       
    92 	}
       
    93 
       
    94 /**
       
    95 This function attempts to connect to a remote device
       
    96 First it does a device discovery using the UI notifier
       
    97 Second it does an SDP search
       
    98 Third it will make the socket connection using AttemptConnection()
       
    99 Failure at any stage is reported via the HandleConnectFailed() callback
       
   100 Success is reported with the HandleNewConnection() callback
       
   101 @param aSocketOwner the object which will take ownership of allocated socket.
       
   102 */
       
   103 EXPORT_C void CBtClient::ConnectToRemoteDeviceL(MBluetoothSocketNotifier& aSocketOwner)
       
   104 	{
       
   105 	LOG_FN_TRACE((_L("+CBtClient::ConnectToRemoteDeviceL")));
       
   106 	TBTDevAddr addr;
       
   107 	TBTDeviceSelectionParamsPckg params;
       
   108 	TBTDeviceResponseParamsPckg result;
       
   109 	params().SetUUID(iServiceUUID); //As far as I know, no UI currently implements this yet.
       
   110 
       
   111 	//use the notifier server to select a device.
       
   112 	RNotifier notif;
       
   113 	User::LeaveIfError(notif.Connect());
       
   114 
       
   115 	TRequestStatus stat;
       
   116     notif.StartNotifierAndGetResponse(stat, KDeviceSelectionNotifierUid, params, result);
       
   117 
       
   118 	User::WaitForRequest(stat);
       
   119 	notif.Close();
       
   120 	User::LeaveIfError(stat.Int());
       
   121 
       
   122 	addr = result().BDAddr();
       
   123 
       
   124 	ConnectToRemoteDeviceL(addr, aSocketOwner);
       
   125 
       
   126 	LOG_FN_TRACE((_L("-CBtClient::ConnectToRemoteDeviceL")));
       
   127 	}
       
   128 
       
   129 /**
       
   130 This function attempts to connect to a remote device
       
   131 First it does an SDP search
       
   132 Second it will make the socket connection using AttemptConnection()
       
   133 Failure at any stage is reported via the HandleConnectFailed() callback
       
   134 Success is reported with the HandleNewConnection() callback
       
   135 @param aAddr the bluetooth address of remote device
       
   136 @param aSocketOwner the object which will take ownership of allocated socket.
       
   137 @note use this version if you already know the device to connect to, e.g. you made your own discovery UI using RHostResolver
       
   138 */
       
   139 EXPORT_C void CBtClient::ConnectToRemoteDeviceL(TBTDevAddr& aAddr, MBluetoothSocketNotifier& aSocketOwner)
       
   140 	{
       
   141 	LOG_FN_TRACE((_L("+CBtClient::ConnectToRemoteDeviceL")));
       
   142 	iConnectingToDevice = aAddr;
       
   143 	//First, we need to perform an SDP search to discover what port the service is running on
       
   144 	__ASSERT_ALWAYS(iSdpAgent == NULL && iConnectionSocket == NULL && iSocketOwner == NULL, ExBtSocket::Panic(ExBtSocket::EReEntrant));
       
   145 	iSocketOwner = &aSocketOwner;
       
   146 	iSdpAgent = CSdpAgent::NewL(*this, aAddr);
       
   147 
       
   148 	//Create a search pattern so we only see SDP records for our application
       
   149 	CSdpSearchPattern *pattern = CSdpSearchPattern::NewL();
       
   150 	CleanupStack::PushL(pattern);
       
   151 	pattern->AddL(iServiceUUID);
       
   152 	iSdpAgent->SetRecordFilterL(*pattern);
       
   153 	CleanupStack::PopAndDestroy(pattern);
       
   154 
       
   155 	//Create a search pattern for the SDP attributes we are interested in - only protocol descriptor in this case
       
   156 	CSdpAttrIdMatchList* attribs = CSdpAttrIdMatchList::NewL();
       
   157 	CleanupStack::PushL(attribs);
       
   158 	attribs->AddL(TAttrRange(KSdpAttrIdProtocolDescriptorList));
       
   159 	iSdpAgent->SetAttributePredictorListL(*attribs);
       
   160 	CleanupStack::PopAndDestroy(attribs);
       
   161 	
       
   162 	//Start the search...
       
   163 	iSdpAgent->NextRecordRequestL();
       
   164 	//Results will be in NextRecordRequestComplete()
       
   165 	LOG_FN_TRACE((_L("-CBtClient::ConnectToRemoteDeviceL")));
       
   166 	}
       
   167 
       
   168 /**
       
   169 This function is called after SDP search is completed.
       
   170 It is used to allocate an appropriate socket and start the connection procedure
       
   171 */
       
   172 void CBtClient::AttemptConnection(TUUID aProtocol, TUint16 aPort)
       
   173 	{
       
   174 	LOG_FN_TRACE((_L("+CBtClient::AttemptConnection")));
       
   175 	//Convert protocol from a UUID (SDP format) to a TUint (ESOCK format)
       
   176 	TUint protocol;
       
   177 	TUint socktype;
       
   178 	TBTSockAddr* addrCommon;
       
   179 	//protocol specific initialisation
       
   180 	if(aProtocol == TUUID(KL2CAP))
       
   181 		{
       
   182 		protocol = KL2CAP;
       
   183 		socktype = KSockSeqPacket;
       
   184 		LOG_INFO((_L("Creating an L2CAP socket")));
       
   185 		iAddrL2CAP.SetPort(aPort);
       
   186 		addrCommon = &iAddrL2CAP;
       
   187 		}
       
   188 	else if(aProtocol == TUUID(KRFCOMM))
       
   189 		{
       
   190 		protocol = KRFCOMM;
       
   191 		socktype = KSockStream;
       
   192 		LOG_INFO((_L("Creating an RFCOMM socket")));
       
   193 		iAddrRFCOMM.SetPort(aPort);
       
   194 		addrCommon = &iAddrRFCOMM;
       
   195 		}
       
   196 	else
       
   197 		{
       
   198 		CallOut(KErrNotSupported);
       
   199 		return;
       
   200 		}
       
   201 	//Common initialisation
       
   202 	addrCommon->SetBTAddr(iConnectingToDevice);
       
   203 	addrCommon->SetSecurity(iSecurityRequirements);
       
   204 
       
   205 	//Create the socket and make an outgoing connection
       
   206 	TRAPD(err, iConnectionSocket = CBluetoothSocket::NewL(*iSocketOwner, iSocketServer, socktype, protocol));
       
   207 	if(err != KErrNone)
       
   208 		{
       
   209 		CallOut(err);
       
   210 		return;
       
   211 		}
       
   212 	iConnectionSocket->Connect(*addrCommon);
       
   213 
       
   214 	//The current API doesnt allow the MBluetoothSocketNotifier to be changed after construction.
       
   215 	//Therefore, the client must deal with connection success or failure
       
   216 	NewConnection();
       
   217 	LOG_FN_TRACE((_L("-CBtClient::AttemptConnection")));
       
   218 	}
       
   219 
       
   220 /**
       
   221 Called to clean up after we failed to make a connection for some reason
       
   222 e.g. missing SDP record.
       
   223 Reset the object to initial state, then callback the owner
       
   224 */
       
   225 void CBtClient::ConnectFailed(TInt aError)
       
   226 	{
       
   227 	LOG_FN_TRACE((_L("+CBtClient::ConnectFailed")));
       
   228 	//Connection failed, attempt to clean up.
       
   229 	delete iConnectionSocket;
       
   230 	iConnectionSocket = NULL;
       
   231 	delete iSdpAgent;
       
   232 	iSdpAgent = NULL;
       
   233 	iSocketOwner = NULL;
       
   234 	//and inform owner
       
   235 	iOwner.HandleConnectFailed(aError);
       
   236 	LOG_FN_TRACE((_L("-CBtClient::ConnectFailed")));
       
   237 	}
       
   238 
       
   239 /**
       
   240 Called when we made a connection successfully.
       
   241 The HandleConnectComplete() from CBluetoothSocket will be sent to the owning object.
       
   242 In this function, we transfer the CBluetoothSocket pointer to our owner, and perform clean up.
       
   243 */
       
   244 void CBtClient::NewConnection()
       
   245 	{
       
   246 	LOG_FN_TRACE((_L("+CBtClient::NewConnection")));
       
   247 	delete iSdpAgent;
       
   248 	iSdpAgent = NULL;
       
   249 	//Connection complete
       
   250 	CBluetoothSocket *socket = iConnectionSocket;
       
   251 	iConnectionSocket = NULL; //ownership transferred	
       
   252 	iOwner.HandleNewConnection(socket);
       
   253 	iSocketOwner = NULL;
       
   254 	LOG_FN_TRACE((_L("-CBtClient::NewConnection")));
       
   255 	}
       
   256 
       
   257 /**
       
   258 Asynchronously call back this object.
       
   259 This is done to avoid deleting the SDP agent from within one of its callbacks.
       
   260 
       
   261 Called with KErrNone, it will schedule AttemptConnection()
       
   262 Called with an error, it will schedule ConnectFailed()
       
   263 */
       
   264 void CBtClient::CallOut(TInt aErr)
       
   265 	{
       
   266 	LOG_FN_TRACE((_L("+CBtClient::CallOut")));
       
   267 	SetActive();
       
   268 	TRequestStatus* stat = &iStatus;
       
   269 	User::RequestComplete(stat, aErr);
       
   270 	LOG_FN_TRACE((_L("-CBtClient::CallOut")));
       
   271 	}
       
   272 
       
   273 //virtual functions from CActive
       
   274 
       
   275 /**
       
   276 This is the callback from CallOut()
       
   277 Called with KErrNone, it will run AttemptConnection()
       
   278 Called with an error, it will run ConnectFailed()
       
   279 */
       
   280 void CBtClient::RunL()
       
   281 	{
       
   282 	LOG_FN_TRACE((_L("+CBtClient::RunL")));
       
   283 	delete iSdpAgent; // done with this
       
   284 	iSdpAgent = NULL;
       
   285 	if(iStatus==KErrNone)
       
   286 		{
       
   287 		AttemptConnection(iParser.iProtocol, static_cast<TUint16>(iParser.iPort));
       
   288 		iParser.iFoundFlags = 0;
       
   289 		}
       
   290 	else
       
   291 		{
       
   292 		ConnectFailed(iStatus.Int());
       
   293 		}
       
   294 	LOG_FN_TRACE((_L("-CBtClient::RunL")));
       
   295 	}
       
   296 
       
   297 /**
       
   298 pure virtual function of CActive
       
   299 This function should cancel any outstanding asynchronous requests.
       
   300 However, this class doesn't make any asynchronous requests, it only completes itself.
       
   301 So there is guarunteed not to be a request outstanding when this is called.
       
   302 */
       
   303 void CBtClient::DoCancel()
       
   304 	{
       
   305 	ASSERT(iStatus != KRequestPending);
       
   306 	}
       
   307 
       
   308 //Virtual functions from MSdpAgentNotifier
       
   309 
       
   310 /**
       
   311 This function is a callback from SDP when it has found or failed to find a matching service record
       
   312 In this example, we assume there will be only one good matching record (valid for most applications)
       
   313 */
       
   314 #ifdef _DEBUG
       
   315 void CBtClient::NextRecordRequestComplete(TInt aError, TSdpServRecordHandle aHandle, TInt aTotalRecordsCount)
       
   316 #else
       
   317 void CBtClient::NextRecordRequestComplete(TInt aError, TSdpServRecordHandle aHandle, TInt /*aTotalRecordsCount*/)
       
   318 #endif
       
   319 	{
       
   320 	LOG_FN_TRACE((_L("+CBtClient::NextRecordRequestComplete")));
       
   321 #ifdef _DEBUG
       
   322 	LOG_INFO((_L("SDP record found, err=%d, handle=%d, count=%d"), aError, aHandle, aTotalRecordsCount));
       
   323 #endif
       
   324 	if(aError == KErrNone)
       
   325 		{
       
   326 		//We have found a service, query its port
       
   327 		TRAP(aError, iSdpAgent->AttributeRequestL(aHandle, KSdpAttrIdProtocolDescriptorList));
       
   328 		}
       
   329 	else if(aError == KErrEof)
       
   330 		{
       
   331 		if(iParser.iFoundFlags != (TBtClientSdpProtocolListParser::EFoundPort | TBtClientSdpProtocolListParser::EFoundProtocol))
       
   332 			{
       
   333 			aError = KErrNotFound;
       
   334 			}
       
   335 		else
       
   336 			{
       
   337 			aError = KErrNone;
       
   338 			CallOut(KErrNone);
       
   339 			}
       
   340 		}
       
   341 	if(aError != KErrNone)
       
   342 		{
       
   343 		CallOut(aError);
       
   344 		}
       
   345 	LOG_FN_TRACE((_L("-CBtClient::NextRecordRequestComplete")));
       
   346 	}
       
   347 
       
   348 /**
       
   349 This function is a callback from SDP when it finds a matching service attribute
       
   350 The attribute value is transferred to us so we must delete it when finished with it
       
   351 */
       
   352 #ifdef _DEBUG
       
   353 void CBtClient::AttributeRequestResult(TSdpServRecordHandle aHandle, TSdpAttributeID aAttrID, CSdpAttrValue* aAttrValue)
       
   354 #else
       
   355 void CBtClient::AttributeRequestResult(TSdpServRecordHandle /*aHandle*/, TSdpAttributeID aAttrID, CSdpAttrValue* aAttrValue)
       
   356 #endif
       
   357 	{
       
   358 	LOG_FN_TRACE((_L("+CBtClient::AttributeRequestResult")));
       
   359 #ifdef _DEBUG
       
   360 	LOG_INFO((_L("SDP attribute found, handle=%d, id=0x%x"), aHandle, aAttrID));
       
   361 #endif
       
   362 
       
   363 	if(aAttrID == KSdpAttrIdProtocolDescriptorList)
       
   364 		{
       
   365 		//Decode the SDP attribute to find protocol and port
       
   366 		TRAPD(err, aAttrValue->AcceptVisitorL(iParser));
       
   367 		if(err != KErrNone)
       
   368 			{
       
   369 			CallOut(err);
       
   370 			}
       
   371 		}
       
   372 	delete aAttrValue;
       
   373 	LOG_FN_TRACE((_L("-CBtClient::AttributeRequestResult")));
       
   374 	}
       
   375 
       
   376 /**
       
   377 The function is a callback from SDP after fully processing a service attribute
       
   378 We schedule a search for the next matching service record.
       
   379 */
       
   380 void CBtClient::AttributeRequestComplete(TSdpServRecordHandle, TInt /*aError*/)
       
   381 	{
       
   382 	LOG_FN_TRACE((_L("+CBtClient::AttributeRequestComplete")));
       
   383 	TRAPD(err, iSdpAgent->NextRecordRequestL());
       
   384 	if(err != KErrNone)
       
   385 		{
       
   386 		CallOut(err);
       
   387 		}
       
   388 	LOG_FN_TRACE((_L("-CBtClient::AttributeRequestComplete")));
       
   389 	}
       
   390 
       
   391 /**
       
   392 Constructor
       
   393 */
       
   394 TBtClientSdpProtocolListParser::TBtClientSdpProtocolListParser()
       
   395 	{
       
   396 	iFoundFlags = 0;
       
   397 	}
       
   398 
       
   399 /**
       
   400 This function is a callback from SDP which is called with each element of the protocol descriptor list
       
   401 we are parsing.
       
   402 There will be one or more protocol UUIDs, received in lower -> higher layer order.
       
   403 We are interested only in the highest layer protocol, so overwrite each time.
       
   404 For the highest layer protocol, there will be connection parameters (a port number).
       
   405 This implementation only supports L2CAP and RFCOMM, so other protocols will cause a KErrNotSupported exception.
       
   406 */
       
   407 void TBtClientSdpProtocolListParser::VisitAttributeValueL(CSdpAttrValue &aValue, TSdpElementType aType)
       
   408 	{
       
   409 	LOG_FN_TRACE((_L("+TBtClientSdpProtocolListParser::VisitAttributeValueL")));
       
   410 	switch(aType)
       
   411 		{
       
   412 		case ETypeDES:
       
   413 		case ETypeDEA:
       
   414 			break;
       
   415 		case ETypeUUID:
       
   416 			iProtocol = aValue.UUID();
       
   417 			iFoundFlags |= EFoundProtocol;
       
   418 			if(iProtocol == TUUID(KL2CAP))
       
   419 				{
       
   420 				LOG_INFO((_L("Found L2CAP protocol")));
       
   421 				}
       
   422 			else if(iProtocol == TUUID(KRFCOMM))
       
   423 				{
       
   424 				LOG_INFO((_L("Found RFCOMM protocl")));
       
   425 				}
       
   426 			else
       
   427 				{
       
   428 				LOG_ERROR((_L("Found unsupported protocol")));
       
   429 				User::Leave(KErrNotSupported);
       
   430 				}
       
   431 			break;
       
   432 		case ETypeUint:
       
   433 			if(aValue.DoesIntFit())
       
   434 				{
       
   435 				iPort = aValue.Uint();
       
   436 				iFoundFlags |= EFoundPort;
       
   437 				LOG_INFO((_L("Found port %d"), iPort));
       
   438 				}
       
   439 			else
       
   440 				{
       
   441 				LOG_ERROR((_L("Port won't fit in 32 bits")));
       
   442 				User::Leave(KErrNotSupported);
       
   443 				}
       
   444 			break;
       
   445 		default:
       
   446 			LOG_ERROR((_L("Found unexpected attribute type %d"), aType));
       
   447 			User::Leave(KErrNotSupported);
       
   448 			break;
       
   449 		}
       
   450 	LOG_FN_TRACE((_L("+TBtClientSdpProtocolListParser::VisitAttributeValueL")));
       
   451 	}
       
   452 
       
   453 /**
       
   454 Callback from SDP at the start of each DEA or DES in the protocol descriptor list.
       
   455 In this implementation we don't care about structure so it is ignored
       
   456 */
       
   457 void TBtClientSdpProtocolListParser::StartListL(CSdpAttrValueList& /*aList*/)
       
   458 	{
       
   459 	//do nothing
       
   460 	}
       
   461 
       
   462 /**
       
   463 Callback from SDP at the end of each DEA or DES in the protocol descriptor list.
       
   464 In this implementation we don't care about structure so it is ignored
       
   465 */
       
   466 void TBtClientSdpProtocolListParser::EndListL()
       
   467 	{
       
   468 	//do nothing
       
   469 	}
       
   470