bluetooth/btsdp/agent/requester.cpp
changeset 0 29b1cd4cb562
equal deleted inserted replaced
-1:000000000000 0:29b1cd4cb562
       
     1 // Copyright (c) 2000-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 //
       
    15 
       
    16 #include <e32math.h>
       
    17 #include <utf.h>
       
    18 #include <es_sock.h>
       
    19 #include <btsdp.h>
       
    20 #include "ipcinternals.h"
       
    21 #include "sdpconsts.h"
       
    22 #include "sdputil.h"
       
    23 #include "SDPDatabase.h"
       
    24 #include "DataEncoder.h"
       
    25 #include "SDPAttribute.h"
       
    26 #include "EncoderVisitor.h"
       
    27 #include "ExtractorVisitor.h"
       
    28 #include "agtypes.h"
       
    29 #include "agutil.h"
       
    30 #include "agconsts.h"
       
    31 #include "ProtocolWrapper.h"
       
    32 #include "requester.h"
       
    33 #include "engine.h"
       
    34 
       
    35 
       
    36 void CSdpRequesterBase::NewRequestL(const TBTDevAddr& aRemDev)
       
    37 /**
       
    38 	Attempts to connect to the remote device. 
       
    39 	This is the first thing to do on receipt of a new request. 
       
    40 **/
       
    41 	{
       
    42 	//If active, must only be active on the idle timer in idle state.
       
    43 	__ASSERT_ALWAYS(iState == EIdle || !IsActive(), AgPanic(ESdpAgentTwoRequests));
       
    44 	if(iState != EDisconnected && aRemDev == iRemoteAddress)
       
    45 		{// Already Connected. Push us through to process state.
       
    46 		Cancel(); //NB iState should be EIdle if we are here
       
    47 		iState = EDisconnected;
       
    48 		TRequestStatus* pS = &iStatus;
       
    49 		User::RequestComplete(pS, KErrNone);
       
    50 		SetActive();
       
    51 		return;
       
    52 		}
       
    53 	iRetryCount = 0;
       
    54 	iRemoteAddress = aRemDev;
       
    55 	ConnectL();
       
    56 	}
       
    57 
       
    58 void CSdpRequesterBase::ConnectL()
       
    59 /**
       
    60 	Ask to connect to remote device address currently set-up in this active object.
       
    61 	Note that we ensure that we are disconnected first...we could
       
    62 	be connected to a different device.
       
    63 **/
       
    64 	{
       
    65 	Disconnect();
       
    66 	User::LeaveIfError(iIdleTimer.CreateLocal());
       
    67 	User::LeaveIfError(iRequester.Open(iSdpSession));
       
    68 	iRequester.Connect(iRemoteAddress, iStatus);
       
    69 	SetActive();
       
    70 	}
       
    71 
       
    72 void CSdpRequesterBase::Disconnect()
       
    73 /**
       
    74 	Bring down a currently existing connection.
       
    75 **/
       
    76 	{
       
    77 	iState = EDisconnected;
       
    78 	iIdleTimer.Close();
       
    79 	iRequester.Close();
       
    80 	}
       
    81 
       
    82 void CSdpRequesterBase::RetrieveResponseL()
       
    83 /**
       
    84 	The initial response to a search or attribute request contains
       
    85 	a buffer size. To obtain the actual result, the response buffer
       
    86 	has to be made big enough, and then have the actual result copied 
       
    87 	into it from the server.
       
    88 	RetrieveResponseL() does just this.
       
    89 **/
       
    90 	{
       
    91 	if(!iResponseHBuf ||
       
    92 		iResponseHBuf->Size() < iResultSize)
       
    93 		{
       
    94 		delete iResponseHBuf;
       
    95 		iResponseHBuf = 0;
       
    96 		iResponseHBuf = HBufC8::NewMaxL(iResultSize);
       
    97 		}
       
    98 	iResponse.Set(iResponseHBuf->Des());
       
    99 
       
   100 	User::LeaveIfError(iRequester.RetrieveResult(iResponse));
       
   101 	}
       
   102 
       
   103 CSdpRequesterBase::	CSdpRequesterBase(RSdpSession& aSdpSession, CSdpAgentEng& aParent)
       
   104 : CActive(CActive::EPriorityStandard),
       
   105  iSdpSession(aSdpSession),
       
   106  iParent(aParent),
       
   107  iRemoteAddress(0),
       
   108  iResponse(0,0)
       
   109 /**
       
   110 	Constructor: This is where ESock session and the CSdpAgentEng parent are provided.
       
   111 	Other member variables are given default values.
       
   112 **/
       
   113 	{
       
   114 	}
       
   115 
       
   116 CSdpRequesterBase::~CSdpRequesterBase()
       
   117 /**
       
   118 	Destructor: Deletes buffer for responses. Ensures we are disconnected.
       
   119 **/
       
   120 	{
       
   121 	delete iResponseHBuf;
       
   122 	Disconnect();
       
   123 	}
       
   124 
       
   125 
       
   126 void CSdpRequesterBase::RunL()
       
   127 /**
       
   128 	Runs state machine.
       
   129 	If no errors then:
       
   130 	@verbatim
       
   131 	state disconnected		->		state requesting
       
   132 									The request was stored before
       
   133 									connection, and now is made.
       
   134 	state requesting		->		state idle.
       
   135 									The request has completed. See
       
   136 									if any further requests come in
       
   137 									over a brief period.
       
   138 	state idle				->		state disconnected 
       
   139 									if idle state interrupted by new request,
       
   140 									this is notional - the RunL is immediately
       
   141 									called again to set state to requesting
       
   142 									and issue the actual request.
       
   143 	@endverbatim
       
   144 **/
       
   145 	{
       
   146 	if(iStatus != KErrNone)
       
   147 		{
       
   148 		if(iState == ERequesting && 
       
   149 		   ++iRetryCount < KRetryLimit)
       
   150 			{// Comms Error in request. Try to connect again.
       
   151 			ConnectL();
       
   152 			return;
       
   153 			}
       
   154 		User::Leave(iStatus.Int());
       
   155 		}
       
   156 
       
   157 	switch(iState)
       
   158 		{
       
   159 	case EDisconnected: //Now connected
       
   160 		iState = ERequesting;
       
   161 		IssueRequestL();
       
   162 		break;
       
   163 	case ERequesting: // Actual request complete
       
   164 		iState = EIdle;
       
   165 		iIdleTimer.After(iStatus, KSdpAgentIdleTimeout);
       
   166 		SetActive();
       
   167 		//RequestCompleteL() might DELETE us, 
       
   168 		//so it must be called last (before 'return').
       
   169 		RequestCompleteL();
       
   170 		return; 
       
   171 	case EIdle:
       
   172 		Reset();
       
   173 		break;
       
   174 		}
       
   175 	}
       
   176 void CSdpRequesterBase::Reset()
       
   177 	{
       
   178 	Disconnect(); //Causes iState to become EDisconnected.
       
   179 	delete iResponseHBuf; //A chance to delete this - it may have become quite large
       
   180 	iResponseHBuf = 0;
       
   181 	iRetryCount = 0;
       
   182 	}
       
   183 
       
   184 void CSdpRequesterBase::DoCancel()
       
   185 /**
       
   186 	Cancels current asynchronous request.
       
   187 	This will be either an SDP query being performed 
       
   188 	through ESock or, if this CSdpRequesterBase is 
       
   189 	in the idle state, a timer (set-up to perform
       
   190 	a disconnection after a defined length of time).
       
   191 **/
       
   192 	{
       
   193 	if(iState == EIdle)
       
   194 		{
       
   195 		__ASSERT_DEBUG(iIdleTimer.Handle(), AgPanic(ESdpAgentBadStateAtCancel));
       
   196 		if(iIdleTimer.Handle())
       
   197 			{
       
   198 			iIdleTimer.Cancel();
       
   199 			}
       
   200 		}
       
   201 	else
       
   202 		{ 
       
   203 		__ASSERT_DEBUG(iRequester.IsOpen(), AgPanic(ESdpAgentBadStateAtCancel));
       
   204 		if(iRequester.IsOpen())
       
   205 			{
       
   206 			iRequester.Cancel();
       
   207 			}
       
   208 		}
       
   209 	}
       
   210 
       
   211 CSdpSearchRequester* CSdpSearchRequester::NewL(RSdpSession& aSdpSession, CSdpAgentEng& aParent)
       
   212 /**
       
   213 	Standard NewL method.
       
   214 	Note: need to pass the client side object of the SDP service session,
       
   215 	and the CSdpA.gentEng parent which is managing this CSdpAttributeRequester 
       
   216 	active object.
       
   217 **/
       
   218 	{
       
   219 	CSdpSearchRequester* self = new(ELeave) CSdpSearchRequester(aSdpSession, aParent);
       
   220 	CleanupStack::PushL(self);
       
   221 	self->ConstructL();
       
   222 	CleanupStack::Pop();
       
   223 	return self;
       
   224 	}
       
   225 
       
   226 CSdpSearchRequester::~CSdpSearchRequester()
       
   227 /**
       
   228 	Destructor
       
   229 **/
       
   230 	{
       
   231 	Cancel();
       
   232 	}
       
   233 
       
   234 void CSdpSearchRequester::SearchRequestL(const TBTDevAddr& aRemoteDev,
       
   235 										 CSdpSearchPattern& aUUIDFilter,
       
   236 										 TUint16 aMaxRecCount, 
       
   237 										 const TDesC8& aContState)
       
   238 /**
       
   239 	Sets up appropriate member variables, then attempts to connect to remote device.
       
   240 	(If connection is made the state machine takes over to make the actual 
       
   241 	search request.)
       
   242 **/
       
   243 	{
       
   244 	__ASSERT_DEBUG(iUUIDFilter==NULL,AgPanic(ESdpAgentTwoRequests));
       
   245 	iUUIDFilter  = &aUUIDFilter;
       
   246 	iMaxRecCount = aMaxRecCount;
       
   247 	iContState.Set(aContState);
       
   248 	NewRequestL(aRemoteDev);
       
   249 	}
       
   250 
       
   251 
       
   252 CSdpSearchRequester::CSdpSearchRequester(RSdpSession& aSdpSession, CSdpAgentEng& aParent)
       
   253 :CSdpRequesterBase(aSdpSession, aParent),
       
   254  iContState(0,0)
       
   255 /**
       
   256 	Constructor: Adds this CSdpAttributeRequester to Active Scheduler.
       
   257 	Sets parent, and passes client side object for 
       
   258 	session with SDP server (currently ESock).
       
   259 	Sets up cont state member to default 0 length descriptor. 
       
   260 **/
       
   261 	{
       
   262 	CActiveScheduler::Add(this);
       
   263 	}
       
   264 
       
   265 void CSdpSearchRequester::ConstructL()
       
   266 /**
       
   267 	Currently does nothing.
       
   268 **/
       
   269 	{
       
   270 	}
       
   271 
       
   272 
       
   273 void CSdpSearchRequester::IssueRequestL()
       
   274 /**
       
   275 	Requester is now connected.
       
   276 	Called down from requester base class.
       
   277 	Time to actually issue the request
       
   278 **/
       
   279 	{
       
   280 	iRequester.SdpSearchRequestL(iResultSize, 
       
   281 								 *iUUIDFilter,
       
   282 								 iMaxRecCount,
       
   283 								 iContState, 
       
   284 								 iStatus);
       
   285 	SetActive();
       
   286 	}
       
   287 
       
   288 void CSdpSearchRequester::RequestCompleteL()
       
   289 /**
       
   290 	Response parameter format is
       
   291  @verbatim
       
   292 		Total service record count		TUint16
       
   293 		Current service record count	TUint16
       
   294 		Service Record Handles			unadorned list of TUint32's
       
   295 		Continuation State				1 + 0-16 bytes
       
   296  @endverbatim
       
   297 
       
   298 **/
       
   299 	{
       
   300 	RetrieveResponseL(); // Use base class to suck result over IPC. Loads iResponse
       
   301 	iUUIDFilter = NULL; // Indicates request is complete
       
   302 	const TInt KRspTotalRecCountOffset = 0;
       
   303 	const TInt KRspCurrentRecCountOffset = 2;
       
   304 	const TInt KRspRecHandlesOffset = 4;
       
   305 	const TInt KMinRspLength = 5; // From Spec. 2 + 2 + 1
       
   306 
       
   307 	if(iResponse.Length() < KMinRspLength)
       
   308 		User::Leave(KErrSdpBadResultData);
       
   309 //
       
   310 	TUint16 totalRecCount = BigEndian::Get16(&iResponse[KRspTotalRecCountOffset]);
       
   311 	TUint16 currentRecCount = BigEndian::Get16(&iResponse[KRspCurrentRecCountOffset]);
       
   312 	if(totalRecCount < currentRecCount)
       
   313 		User::Leave(KErrSdpBadResultData);
       
   314 	TInt byteCount = currentRecCount*sizeof(TSdpServRecordHandle);
       
   315 	if(byteCount + KRspRecHandlesOffset + 1 > iResponse.Length())
       
   316 		User::Leave(KErrSdpBadResultData);
       
   317 //
       
   318 	TInt contLen = iResponse[KRspRecHandlesOffset+byteCount];
       
   319 	if(contLen > KSdpContinuationStateMaxLength ||
       
   320 	   KRspRecHandlesOffset + byteCount + 1 + contLen != iResponse.Length())
       
   321 		User::Leave(KErrSdpBadResultData);
       
   322 //
       
   323 	TPtrC8 recHandles(&iResponse[KRspRecHandlesOffset], byteCount);
       
   324 	TPtrC8 contState;
       
   325 	if(iResponse.Length()>KRspRecHandlesOffset + 1 + byteCount && contLen)
       
   326 		{
       
   327 		contState.Set(&iResponse[KRspRecHandlesOffset + 1 + byteCount], contLen);
       
   328 		}
       
   329 	else
       
   330 		{
       
   331 		contState.Set(0,0);
       
   332 		}
       
   333 
       
   334 	//Call this last: it may well end up calling back into this
       
   335 	//CSdpSearchRequester object.
       
   336 	iParent.HandleServiceSearchResponseL(totalRecCount, 
       
   337 										 currentRecCount, 
       
   338 										 recHandles, 
       
   339 										 contState);
       
   340 	}
       
   341 
       
   342 void CSdpSearchRequester::Reset()
       
   343 /**
       
   344 	Called when base requester idle timer expires,
       
   345 	and when an error is reported
       
   346 	Cleans up and makes sure we really are disconnected.
       
   347 **/
       
   348 	{
       
   349 	CSdpRequesterBase::Reset();
       
   350 	}
       
   351 
       
   352 void CSdpSearchRequester::DoCancel()
       
   353 /**
       
   354 	Cancels current asynchronous requests.
       
   355 	This could be with the Sdp server (currently ESOCK)
       
   356 	or if this CSdpRequesterBase is in idle state
       
   357 	it cancels the time out timer (set-up to perform
       
   358 	a disconnection after a defined length of time).
       
   359 **/
       
   360 	{
       
   361 	CSdpRequesterBase::DoCancel();
       
   362 
       
   363 	// We only need to reset iUUIDFilter if the Cancel
       
   364 	// has been called externally.
       
   365 	// If the state is EIdle, either iUUIDFilter is already NULL
       
   366 	// or the Cancel is being called internally by NewRequestL 
       
   367 	if(iState != EIdle)
       
   368 		{
       
   369 		iUUIDFilter = NULL; // Indicates request is complete
       
   370 		}
       
   371 	}
       
   372 
       
   373 TInt CSdpSearchRequester::RunError(TInt aError)
       
   374 /**
       
   375 	Called automatically when a RunL leaves.
       
   376 	Calls appropriate error handling function in the CSdpAgentEng parent.
       
   377 **/
       
   378 	{
       
   379 	/*
       
   380 	Make sure we are not active. RunL can leave after SetActive is called.
       
   381 	*/
       
   382 	Cancel(); 
       
   383 
       
   384 	/*
       
   385 	Indicates request has completed - albeit with an error.
       
   386 	Do NOT rely on Cancel to have called DoCancel to do this.
       
   387 	It will only do this if the CSdpSearchRequester is active.
       
   388 	*/
       
   389 	iUUIDFilter = NULL;
       
   390 
       
   391 	/*
       
   392 	We may or may not be disconnected.
       
   393 	We may not be able to be re-connect
       
   394 	We need to be in a known state after this call.
       
   395 	So make sure we are disconnnected,
       
   396 	and leave it to the user to try to
       
   397 	reconnect. He will do this automatically
       
   398 	by trying to start a new request.
       
   399 	*/
       
   400 	Reset();
       
   401 		
       
   402 	iParent.HandleServiceSearchError(aError);
       
   403 	return KErrNone;
       
   404 	}
       
   405 
       
   406 
       
   407 CSdpAttributeRequester* CSdpAttributeRequester::NewL(RSdpSession& aSdpSession, CSdpAgentEng& aParent)
       
   408 /**
       
   409 	Standard NewL method.
       
   410 	Note: need to pass the client side object of the SDP service session,
       
   411 	and the CSdpA.gentEng parent which is managing this CSdpAttributeRequester 
       
   412 	active object.
       
   413 **/
       
   414 	{
       
   415 	CSdpAttributeRequester* self = new(ELeave) CSdpAttributeRequester(aSdpSession, aParent);
       
   416 	CleanupStack::PushL(self);
       
   417 	self->ConstructL();
       
   418 	CleanupStack::Pop();
       
   419 	return self;
       
   420 	}
       
   421 
       
   422 CSdpAttributeRequester::~CSdpAttributeRequester()
       
   423 /**
       
   424 	Destructor
       
   425 **/
       
   426 	{
       
   427 	Cancel();
       
   428 	}
       
   429 
       
   430 void CSdpAttributeRequester::AttributeRequestL(const TBTDevAddr& aRemoteDev,
       
   431 											   TSdpServRecordHandle aHandle, 
       
   432 											   TUint16 aMaxAttrByteCount,
       
   433 											   CSdpAttrIdMatchList& aMatchList,
       
   434 											   const TDesC8& aContState)
       
   435 /**
       
   436 	Sets up appropriate member variables, then attempts to connect to remote device.
       
   437 	(If connection is made the state machine takes over to make the actual 
       
   438 	attribute request.)
       
   439 **/
       
   440 	{
       
   441 	__ASSERT_DEBUG(iMatchList==NULL,AgPanic(ESdpAgentTwoRequests));
       
   442 	iHandle  = aHandle;
       
   443 	iMaxAttrByteCount = aMaxAttrByteCount;
       
   444 	iMatchList = &aMatchList;
       
   445 	iContState.Set(aContState);
       
   446 	NewRequestL(aRemoteDev);
       
   447 	}
       
   448 
       
   449 
       
   450 CSdpAttributeRequester::CSdpAttributeRequester(RSdpSession& aSdpSession, CSdpAgentEng& aParent)
       
   451 :CSdpRequesterBase(aSdpSession, aParent),
       
   452  iContState(0,0)
       
   453 /**
       
   454 	Constructor: Adds this CSdpAttributeRequester to Active Scheduler.
       
   455 	Sets parent, and passes client side object for 
       
   456 	session with SDP server (currently ESock).
       
   457 	Sets up cont state member to default 0 length descriptor. 
       
   458 **/
       
   459 	{
       
   460 	CActiveScheduler::Add(this);
       
   461 	}
       
   462 
       
   463 void CSdpAttributeRequester::ConstructL()
       
   464 /**
       
   465 	Currently blank.
       
   466 **/
       
   467 	{
       
   468 	}
       
   469 
       
   470 
       
   471 void CSdpAttributeRequester::IssueRequestL()
       
   472 /**
       
   473 	Requester is now connected.
       
   474 	Called down from requester base class.
       
   475 	Time to actually issue the request
       
   476 **/
       
   477 	{
       
   478 	iRequester.SdpAttributeRequestL(iResultSize, 
       
   479 									iHandle,
       
   480 									iMaxAttrByteCount,
       
   481 									*iMatchList, 
       
   482 									iContState, 
       
   483 									iStatus);
       
   484 	SetActive();
       
   485 	}
       
   486 
       
   487 void CSdpAttributeRequester::RequestCompleteL()
       
   488 /**
       
   489 	Response parameter format is
       
   490  @verbatim
       
   491 		byte count of attr list		TUint16
       
   492 		Attribute ID & Value		DES
       
   493 		Continuation State			1 + 0-16 bytes
       
   494  @endverbatim
       
   495 
       
   496 **/
       
   497 	{
       
   498 	RetrieveResponseL(); // Use base class to suck result over IPC. Loads iResponse
       
   499 	iMatchList = NULL; // Indicates request is complete
       
   500 	const TInt KRspTotalCountOffset = 0;
       
   501 	const TInt KRspAttributeListOffset = 2;
       
   502 	const TInt KMinRspLength = 5; // From Spec. 2 + 2 + 1
       
   503 
       
   504 	if(iResponse.Length() < KMinRspLength)
       
   505 		User::Leave(KErrSdpBadResultData);
       
   506 //
       
   507 	TUint16 byteCount = BigEndian::Get16(&iResponse[KRspTotalCountOffset]);
       
   508 	if(byteCount + KRspAttributeListOffset + 1 > iResponse.Length())
       
   509 		User::Leave(KErrSdpBadResultData);
       
   510 //
       
   511 	TInt contLen = iResponse[KRspAttributeListOffset+byteCount];
       
   512 	if(contLen > KSdpContinuationStateMaxLength ||
       
   513 	   KRspAttributeListOffset + byteCount + 1 + contLen != iResponse.Length())
       
   514 		User::Leave(KErrSdpBadResultData);
       
   515 //
       
   516 	TPtrC8 attrList(&iResponse[KRspAttributeListOffset], byteCount);
       
   517 	TPtrC8 contState;
       
   518 	if(iResponse.Length()>KRspAttributeListOffset + 1 + byteCount && contLen)
       
   519 		{
       
   520 		contState.Set(&iResponse[KRspAttributeListOffset + 1 + byteCount], contLen);
       
   521 		}
       
   522 	else
       
   523 		{
       
   524 		contState.Set(0,0);
       
   525 		}
       
   526 
       
   527 	//Call this last: it may well end up calling back into this
       
   528 	//CSdpAttributeRequester object.
       
   529 	iParent.HandleAttributeResponseL(attrList, contState);
       
   530 	}
       
   531 
       
   532 void CSdpAttributeRequester::Reset()
       
   533 /**
       
   534 	Called when base requester idle timer expires,
       
   535 	and when an error is reported
       
   536 	Cleans up and makes sure we really are disconnected.
       
   537 **/
       
   538 	{
       
   539 	CSdpRequesterBase::Reset();
       
   540 	}
       
   541 
       
   542 void CSdpAttributeRequester::DoCancel()
       
   543 /**
       
   544 	Cancels current asynchronous requests.
       
   545 	This could be with the Sdp server (currently ESOCK)
       
   546 	or if this CSdpRequesterBase is in idle state
       
   547 	it cancels the time out timer (set-up to perform
       
   548 	a disconnection after a defined length of time).
       
   549 **/
       
   550 	{
       
   551 	CSdpRequesterBase::DoCancel();
       
   552 
       
   553 	// We only need to reset iMatchList if the Cancel
       
   554 	// has been called externally.
       
   555 	// If the state is EIdle, either iMatchList is already NULL
       
   556 	// or the Cancel is being called internally by NewRequestL 
       
   557 	if(iState != EIdle)
       
   558 		{
       
   559 		iMatchList = NULL; // Indicates request is complete
       
   560 		}
       
   561 	}
       
   562 
       
   563 TInt CSdpAttributeRequester::RunError(TInt aError)
       
   564 /**
       
   565 	Called automatically when a RunL leaves.
       
   566 	Calls appropriate error handling function in the CSdpAgentEng parent.
       
   567 **/
       
   568 	{
       
   569 	/*
       
   570 	Make sure we are not active. RunL can leave after SetActive is called.
       
   571 	*/
       
   572 	Cancel(); 
       
   573 
       
   574 	/*
       
   575 	Indicates request has completed - albeit with an error.
       
   576 	Do NOT rely on Cancel to have called DoCancel to do this.
       
   577 	It will only do this if the CSdpAttributeRequester is active
       
   578 	and the idle timer is not running.
       
   579 	*/
       
   580 	iMatchList = NULL; //indicates request has completed - albeit with an error
       
   581 
       
   582 	/*
       
   583 	We may or may not be disconnected.
       
   584 	We may not be able to be re-connect
       
   585 	We need to be in a known state after this call.
       
   586 	So make sure we are disconnnected,
       
   587 	and leave it to the user to try to
       
   588 	reconnect. He will do this automatically
       
   589 	by trying to start a new request.
       
   590 	*/
       
   591 	Reset();
       
   592 		
       
   593 	iParent.HandleAttributeError(aError);
       
   594 	return KErrNone;
       
   595 	}
       
   596 
       
   597 
       
   598 
       
   599