|
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 |