|
1 // Copyright (c) 1999-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 // Provides the implementation of the subsession that refers to remote devices |
|
15 // |
|
16 // |
|
17 |
|
18 |
|
19 #include "BTManServer.h" |
|
20 #include "btmanserverutil.h" |
|
21 #include <bluetooth/logger.h> |
|
22 |
|
23 #ifdef __FLOG_ACTIVE |
|
24 _LIT8(KLogComponent, LOG_COMPONENT_BT_MANAGER_SERVER); |
|
25 #endif |
|
26 |
|
27 //===================================================================== |
|
28 // CBTRegistrySubSession |
|
29 //===================================================================== |
|
30 //Description: Provides an interface to the registry for device |
|
31 // This is needed to provide device connection information for |
|
32 // subsequent connections. |
|
33 //===================================================================== |
|
34 |
|
35 |
|
36 CBTRegistrySubSession* CBTRegistrySubSession::NewL(CBTManSession& aSession, CBTRegistry& aRegistry) |
|
37 { |
|
38 LOG_STATIC_FUNC |
|
39 CBTRegistrySubSession* self = new(ELeave) CBTRegistrySubSession(aSession, aRegistry); |
|
40 //Since its a CObject derived class so we should use CleanupClosePushL |
|
41 CleanupClosePushL(*self); |
|
42 self->ConstructL(); |
|
43 CleanupStack::Pop(); |
|
44 return self; |
|
45 } |
|
46 |
|
47 void CBTRegistrySubSession::ConstructL() |
|
48 { |
|
49 LOG_FUNC |
|
50 } |
|
51 |
|
52 CBTRegistrySubSession::CBTRegistrySubSession(CBTManSession& aSession, CBTRegistry& aRegistry) |
|
53 : CBTManSubSession(aSession,aRegistry) |
|
54 { |
|
55 LOG_FUNC |
|
56 } |
|
57 |
|
58 CBTRegistrySubSession::~CBTRegistrySubSession() |
|
59 { |
|
60 LOG_FUNC |
|
61 DoCloseView(); |
|
62 delete iResultBuffer; |
|
63 } |
|
64 |
|
65 |
|
66 void CBTRegistrySubSession::OpenViewL(const TBTRegistrySearch& aSearch, const RMessage2& aMessage) |
|
67 /** |
|
68 @param aSearch The structure containing the parameters to search for |
|
69 @param aMessage The message to complete |
|
70 **/ |
|
71 { |
|
72 LOG_FUNC |
|
73 if (iDBView) |
|
74 { |
|
75 // previous view hasn't been retrieved |
|
76 User::Leave(KErrInUse); |
|
77 } |
|
78 |
|
79 // Form the SQL query based on the search criteria |
|
80 RBTDbQuery query; |
|
81 CleanupClosePushL(query); |
|
82 query.SearchL(aSearch); |
|
83 |
|
84 // execute the SQL |
|
85 iDBView = iRegistry.OpenViewL(query, iBookmark); |
|
86 |
|
87 iUnderlyingSearch = query.ConstraintBuf().AllocL(); |
|
88 |
|
89 // see how many records are in the view |
|
90 iViewCount = iDBView->CountL(); |
|
91 |
|
92 // send count back to client |
|
93 if (!iViewCount) |
|
94 { |
|
95 User::Leave(KErrNotFound); |
|
96 } |
|
97 else |
|
98 { |
|
99 // go to the first record in the view for later retrieval |
|
100 iDBView->FirstL(); |
|
101 iSession.CompleteMessage(aMessage, iViewCount); |
|
102 } |
|
103 CleanupStack::Pop(1); // query |
|
104 // **leave view open for client's later requests upon it** |
|
105 } |
|
106 |
|
107 void CBTRegistrySubSession::UnpairL(const TBTDevAddr& aAddress, const RMessage2& aMessage) |
|
108 /** |
|
109 @param aAddress The address to unbond |
|
110 @param aMessage The message to complete |
|
111 **/ |
|
112 { |
|
113 LOG_FUNC |
|
114 // Form the SQL query based on the device address |
|
115 RBTDbQuery query; |
|
116 CleanupClosePushL(query); |
|
117 query.FindDeviceL(aAddress); |
|
118 |
|
119 // Execute the SQL |
|
120 RDbView* localView = iRegistry.OpenViewL(query, iBookmark); |
|
121 CleanupCloseDeletePushL(localView); |
|
122 |
|
123 HBufC* localViewUnderlyingSearch = query.ConstraintBuf().AllocLC(); |
|
124 |
|
125 TInt c = localView->CountL(); |
|
126 |
|
127 if (c) |
|
128 { |
|
129 // device is there - check to see if client is allowed to change |
|
130 __ASSERT_DEBUG(c==1, PanicServer(EBTManTooManyDevicesInView)); |
|
131 |
|
132 // Try to notify interested parties before deleting the device. |
|
133 // If we notify afterwards, then the device will fall out of someone's |
|
134 // view and a change would then not be detected on that view. |
|
135 NotifyChange(*this, *localViewUnderlyingSearch); |
|
136 |
|
137 // the client is allowed to change the details |
|
138 iRegistry.UnpairL(*localView); |
|
139 iSession.CompleteMessage(aMessage, KErrNone); |
|
140 } |
|
141 else |
|
142 { |
|
143 // device wasn't even there |
|
144 User::Leave(KErrNotFound); |
|
145 } |
|
146 |
|
147 // Cleanup localViewUnderlyingSearch, localView and query |
|
148 CleanupStack::PopAndDestroy(3, &query); |
|
149 } |
|
150 |
|
151 void CBTRegistrySubSession::GetDeviceL(const TBTNamelessDevice& aDevice, const RMessage2& aMessage) |
|
152 /** |
|
153 Get a single nameless device from registry |
|
154 @param aDevice The device to retrieve (only the address is used as a key at present) |
|
155 @param aMessage The message to complete |
|
156 **/ |
|
157 { |
|
158 LOG_FUNC |
|
159 // open the device |
|
160 TDbBookmark bookmark; |
|
161 RDbView* localView = iRegistry.OpenDeviceL(aDevice.Address(), bookmark); |
|
162 CleanupCloseDeletePushL(localView); |
|
163 |
|
164 TInt localCount = localView->CountL(); |
|
165 |
|
166 if (localCount==0) |
|
167 { |
|
168 // the device wasn't there! |
|
169 User::Leave(KErrNotFound); |
|
170 } |
|
171 else |
|
172 { |
|
173 __ASSERT_DEBUG(localCount==1, PanicServer(EBTManTooManyDevicesInView)); |
|
174 |
|
175 // Get the resulting device (we take ownership), stays on cleanupstack |
|
176 |
|
177 |
|
178 const TBTNamelessDevice* device = |
|
179 iRegistry.GetNextNamelessDeviceLC(*localView,bookmark,aMessage.HasCapability(ECapabilityReadDeviceData)); |
|
180 |
|
181 |
|
182 // Write back resulting device to client |
|
183 TBTNamelessDevicePckgBuf writePckg; |
|
184 writePckg = *device; |
|
185 |
|
186 aMessage.WriteL(0, writePckg); |
|
187 iSession.CompleteMessage(aMessage, KErrNone); |
|
188 |
|
189 CleanupStack::Pop(1); // device |
|
190 delete const_cast<TBTNamelessDevice*>(device); //MSDev workaround |
|
191 } |
|
192 CleanupStack::PopAndDestroy(localView); |
|
193 } |
|
194 |
|
195 |
|
196 void CBTRegistrySubSession::AddDeviceL(const CBTDevice& aDetails, const RMessage2& aMessage) |
|
197 /** |
|
198 @note This is an insertion, which can be slower via SQL; so we stick with C++ methods |
|
199 Further, this saves us the effort of converting various binary values into the ASCII |
|
200 SQL equivalent, only for DBMS to have to retokenise it ;-) |
|
201 Slight disadvantage is that the C++ interface to DBMS is synchronous... |
|
202 ..but the addition of a new row unordered should not be hard in our small database |
|
203 **/ |
|
204 { |
|
205 LOG_FUNC |
|
206 |
|
207 iRegistry.CreateDeviceL(aDetails, |
|
208 aMessage.HasCapability(ECapabilityWriteDeviceData), |
|
209 aMessage.SecureId()); |
|
210 // Form the SQL query based on the device address |
|
211 RBTDbQuery query; |
|
212 CleanupClosePushL(query); |
|
213 query.FindDeviceL(aDetails.BDAddr()); |
|
214 |
|
215 HBufC* localViewUnderlyingSearch = query.ConstraintBuf().AllocLC(); |
|
216 |
|
217 iSession.CompleteMessage(aMessage, KErrNone); |
|
218 |
|
219 // Try to notify interested parties after adding the device. |
|
220 // We need the device to be present in other views for change notification to work. |
|
221 NotifyChange(*this, *localViewUnderlyingSearch); |
|
222 |
|
223 // Cleanup localViewUnderlyingSearch and query |
|
224 CleanupStack::PopAndDestroy(2, &query); |
|
225 } |
|
226 |
|
227 void CBTRegistrySubSession::ModifyL(const CBTDevice& aNewDetails, const RMessage2& aMessage) |
|
228 /** |
|
229 @param aNewDetails The new device details; with the address to modify |
|
230 @param aMessage The message to complete |
|
231 **/ |
|
232 { |
|
233 LOG_FUNC |
|
234 // check that we can modify this device... |
|
235 if (!aNewDetails.IsValidBDAddr()) |
|
236 { |
|
237 // the device address has not been set correctly |
|
238 User::Leave(KErrCorrupt); |
|
239 } |
|
240 |
|
241 if (!iRegistry.DevicePresentL(aNewDetails.BDAddr())) |
|
242 { |
|
243 // the device record that the client wants to modify is not present in the registry |
|
244 User::Leave(KErrNotFound); |
|
245 } |
|
246 else |
|
247 { |
|
248 // device is in Registry - open local device view |
|
249 TDbBookmark dummy; // ignore position for now |
|
250 |
|
251 // Form the SQL query based on the device address |
|
252 RBTDbQuery query; |
|
253 CleanupClosePushL(query); |
|
254 query.FindDeviceL(aNewDetails.BDAddr()); |
|
255 |
|
256 // Execute the SQL |
|
257 RDbView* localView = iRegistry.OpenViewL(query, dummy); |
|
258 CleanupCloseDeletePushL(localView); |
|
259 |
|
260 HBufC* localViewUnderlyingSearch = query.ConstraintBuf().AllocLC(); |
|
261 |
|
262 // Try to notify interested parties before making the change. |
|
263 // If we notify afterwards, then the device may fall out of someone's |
|
264 // view and a change would then not be detected on that view. |
|
265 NotifyChange(*this, *localViewUnderlyingSearch); |
|
266 |
|
267 iRegistry.UpdateDeviceL(*localView, aNewDetails); |
|
268 iSession.CompleteMessage(aMessage, KErrNone); |
|
269 |
|
270 // Cleanup localViewUnderlyingSearch, localView and query |
|
271 CleanupStack::PopAndDestroy(3, &query); |
|
272 } |
|
273 } |
|
274 |
|
275 void CBTRegistrySubSession::ModifyL(const TBTNamelessDevice& aNewDetails, const RMessage2& aMessage) |
|
276 /** |
|
277 @param aNewDetails The new device details; with the address to modify |
|
278 @param aMessage The message to complete |
|
279 **/ |
|
280 { |
|
281 LOG_FUNC |
|
282 // make a CBTDevice based on the T class |
|
283 CBTDevice* tempDevice = CBTDevice::NewLC(aNewDetails); |
|
284 ModifyL(*tempDevice, aMessage); |
|
285 CleanupStack::PopAndDestroy(tempDevice); |
|
286 // Notification already handled by overloaded ModifyL() implementation |
|
287 } |
|
288 |
|
289 void CBTRegistrySubSession::ModifyNameL(const TBTDevAddr& aAddress, const RMessage2& aMessage) |
|
290 /** |
|
291 Updates one of the names for a device in the Registry |
|
292 Here we decide if we can work with an address or not |
|
293 |
|
294 @param aAddress The address of the device to modify |
|
295 @param aMessage The message to complete, and from which we get the name |
|
296 **/ |
|
297 { |
|
298 LOG_FUNC |
|
299 |
|
300 // Form the SQL query based on the device address |
|
301 RBTDbQuery query; |
|
302 CleanupClosePushL(query); |
|
303 query.FindDeviceL(aAddress); |
|
304 |
|
305 // Execute the SQL |
|
306 RDbView* localView = iRegistry.OpenViewL(query, iBookmark); |
|
307 CleanupCloseDeletePushL(localView); |
|
308 |
|
309 HBufC* localViewUnderlyingSearch = query.ConstraintBuf().AllocLC(); |
|
310 |
|
311 TInt c = localView->CountL(); |
|
312 |
|
313 if (!c) |
|
314 { |
|
315 // can't modify name cos device isn't there! |
|
316 User::Leave(KErrNotFound); |
|
317 } |
|
318 |
|
319 |
|
320 |
|
321 // read the names |
|
322 TBuf8<KMaxBluetoothNameLen> buf; |
|
323 aMessage.ReadL(1, buf); |
|
324 |
|
325 // Try to notify interested parties before making the change. |
|
326 // If we notify afterwards, then the device may fall out of someone's |
|
327 // view and a change would then not be detected on that view. |
|
328 NotifyChange(*this, *localViewUnderlyingSearch); |
|
329 |
|
330 // read out what's already in the database |
|
331 iRegistry.UpdateNameL(aAddress, buf, static_cast<TBTManServerRequest>(aMessage.Function())); |
|
332 // done, OK at this stage |
|
333 iSession.CompleteMessage(aMessage, KErrNone); |
|
334 |
|
335 // Cleanup localViewUnderlyingSearch, localView and query |
|
336 CleanupStack::PopAndDestroy(3, &query); |
|
337 } |
|
338 |
|
339 void CBTRegistrySubSession::PreLoadL(const RMessage2& aMessage) |
|
340 /** |
|
341 Load in the results from DBMS |
|
342 we'd have to load it all in at some time for giving to client, so we extend |
|
343 the lifetime a bit. We kill the cache upon delivery to client. |
|
344 |
|
345 This approach allows us to compute the size of the result set, |
|
346 so that we can tell client |
|
347 |
|
348 @param aMessage The message to complete |
|
349 **/ |
|
350 { |
|
351 LOG_FUNC |
|
352 if (!iViewCount) |
|
353 { |
|
354 // client has asked for something we dont have |
|
355 User::Leave(KErrNotFound); |
|
356 } |
|
357 |
|
358 // In case a previous registry response is cancelled, and then for the next request, |
|
359 // a new registry response is created, the iResultBuffer could still be non-zero. |
|
360 // So, release memory now. |
|
361 delete iResultBuffer; |
|
362 iResultBuffer = NULL; |
|
363 |
|
364 // Create a buffer to hold the externalised entry |
|
365 iResultBuffer = CBufFlat::NewL(sizeof(CBTDevice)); // just a granularity for expansion |
|
366 |
|
367 RBufWriteStream stream; |
|
368 CleanupClosePushL(stream); |
|
369 stream.Open(*iResultBuffer); // returns void |
|
370 |
|
371 TInt res = KErrNone; |
|
372 CBTDevice* device = NULL; |
|
373 |
|
374 iNumDevicesInBuffer=0; |
|
375 while (res == KErrNone) |
|
376 { |
|
377 // Get the entry and externalise to stream. |
|
378 TRAP(res, |
|
379 device= iRegistry.GetNextDeviceL(*iDBView, |
|
380 iBookmark, |
|
381 aMessage.HasCapability(ECapabilityReadDeviceData)); |
|
382 CleanupStack::PushL(device); |
|
383 device->ExternalizeL(stream); |
|
384 CleanupStack::PopAndDestroy(device); |
|
385 device = NULL; |
|
386 iNumDevicesInBuffer++; |
|
387 |
|
388 ); |
|
389 } |
|
390 |
|
391 // complete message with size of the blob or the error |
|
392 TInt code; |
|
393 if (res == KErrEof) |
|
394 { |
|
395 code = iResultBuffer->Size(); |
|
396 } |
|
397 else |
|
398 { |
|
399 code = res; |
|
400 } |
|
401 iSession.CompleteMessage(aMessage, code); |
|
402 CleanupStack::PopAndDestroy(1); // stream |
|
403 |
|
404 // do not need to close view here |
|
405 } |
|
406 |
|
407 |
|
408 void CBTRegistrySubSession::RetrieveL(const RMessage2& aMessage) |
|
409 /** |
|
410 Returns the previously cached result set to the client |
|
411 |
|
412 @param aMessage The message to complete |
|
413 **/ |
|
414 { |
|
415 LOG_FUNC |
|
416 if (!iResultBuffer) |
|
417 { |
|
418 // they haven't preloaded |
|
419 User::Leave(KErrNotReady); |
|
420 } |
|
421 // write the buffer of devices back |
|
422 aMessage.WriteL(0, iResultBuffer->Ptr(0)); |
|
423 iSession.CompleteMessage(aMessage, iNumDevicesInBuffer); |
|
424 |
|
425 // release memory now to reduce footprint |
|
426 delete iResultBuffer; |
|
427 iResultBuffer = NULL; |
|
428 } |
|
429 |
|
430 void CBTRegistrySubSession::DeleteViewL(const RMessage2& aMessage) |
|
431 /** |
|
432 Deletes all devices in the view - could be used to delete many devices in one call |
|
433 |
|
434 @param aMessage The message to complete |
|
435 **/ |
|
436 { |
|
437 LOG_FUNC |
|
438 if (!iViewCount) |
|
439 { |
|
440 // no point calling delete on a view with no devices contained! |
|
441 User::Leave(KErrNotReady); |
|
442 } |
|
443 |
|
444 // Try to notify interested parties before deleting devices. |
|
445 // If we notify afterwards, then the devices will fall out of someone's |
|
446 // view and a change would then not be detected on that view. |
|
447 NotifyChange(*this, *iUnderlyingSearch); |
|
448 |
|
449 // Delete all in view only if client has W.D.D capability. Otherwise delete the |
|
450 // devices whose SID value is same as the SecureId |
|
451 iRegistry.DeleteViewL( *iDBView, |
|
452 aMessage.HasCapability(ECapabilityWriteDeviceData), |
|
453 aMessage.SecureId()); |
|
454 |
|
455 |
|
456 iSession.CompleteMessage(aMessage, KErrNone); |
|
457 |
|
458 // The view is not null, but probably not of much |
|
459 // use (ie. a view containing devices created by processes other than your own) |
|
460 // In addition, making the closure conditional would mean a potential SC |
|
461 // break for code moving from non-secure to secure world, so DoCloseView |
|
462 // remains non-optional at this point. |
|
463 DoCloseView(); |
|
464 } |
|
465 |
|
466 void CBTRegistrySubSession::UnpairViewL(const RMessage2& aMessage) |
|
467 /** |
|
468 Unpairs all devices in the view |
|
469 |
|
470 @param aMessage The message to complete |
|
471 **/ |
|
472 { |
|
473 LOG_FUNC |
|
474 if (!iViewCount) |
|
475 { |
|
476 // no point calling delete on a view with no devices contained! |
|
477 User::Leave(KErrNotReady); |
|
478 } |
|
479 |
|
480 // Try to notify interested parties before making the change. |
|
481 // If we notify afterwards, then the device may fall out of someone's |
|
482 // view and a change would then not be detected on that view. |
|
483 NotifyChange(*this, *iUnderlyingSearch); |
|
484 |
|
485 iRegistry.UnpairViewL(*iDBView); |
|
486 iSession.CompleteMessage(aMessage, KErrNone); |
|
487 } |
|
488 |
|
489 void CBTRegistrySubSession::CloseView(const RMessage2& aMessage) |
|
490 /** |
|
491 Client has finished with the view |
|
492 |
|
493 @param aMessage The message to complete |
|
494 */ |
|
495 { |
|
496 LOG_FUNC |
|
497 DoCloseView(); |
|
498 iSession.CompleteMessage(aMessage, KErrNone); |
|
499 |
|
500 // no underlying registry change, so don't notify |
|
501 } |
|
502 |
|
503 void CBTRegistrySubSession::DoCloseView() |
|
504 /** |
|
505 Close the internal view if it exists |
|
506 **/ |
|
507 { |
|
508 LOG_FUNC |
|
509 if (iDBView) |
|
510 { |
|
511 iDBView->Close(); |
|
512 delete iDBView; |
|
513 iDBView=NULL; |
|
514 |
|
515 delete iUnderlyingSearch; |
|
516 iUnderlyingSearch=NULL; |
|
517 } |
|
518 // No point having notify on closed view; yet session may have already processed a cancel on it |
|
519 if (iSession.FindMessage(iViewChangeNotificationMessage)) |
|
520 { |
|
521 iSession.CompleteMessage(iViewChangeNotificationMessage, KErrCompletion); |
|
522 } |
|
523 |
|
524 iViewCount=0; |
|
525 } |
|
526 |
|
527 |
|
528 void CBTRegistrySubSession::Cleanup(TInt /*aError*/) |
|
529 { |
|
530 LOG_FUNC |
|
531 // this subsession can help by closing the view |
|
532 // it is unlikely to be in a good state anyway |
|
533 DoCloseView(); |
|
534 } |
|
535 |
|
536 void CBTRegistrySubSession::NotifyChange(CBTManSubSession& aSubSessionViewOwner, const TDesC& aViewDescriptor) |
|
537 /** |
|
538 Called by methods that know they've changed settings on the remotedevice table |
|
539 This method will notify the client that was interested |
|
540 **/ |
|
541 { |
|
542 LOG_FUNC |
|
543 CBTManSubSession::NotifyChange(KRegistryChangeRemoteTable, aSubSessionViewOwner, aViewDescriptor); |
|
544 } |
|
545 |
|
546 TBool CBTRegistrySubSession::IsOverlappingView(const TDesC& aViewDescriptor) |
|
547 { |
|
548 LOG_FUNC |
|
549 // Need to take the view descriptor and test with our view |
|
550 TBool match = EFalse; |
|
551 |
|
552 // We cannot rely on the handle of iViewChangeNotificationMessage to provide an indicator |
|
553 // of whether or not the message is completed, since we have a copy of the RMessage2 held by |
|
554 // the CBTManMessage instance. |
|
555 if (iDBView && iSession.FindMessage(iViewChangeNotificationMessage)) |
|
556 { |
|
557 // Bookmark current place then use at end |
|
558 TDbBookmark bookmark = iDBView->Bookmark(); |
|
559 |
|
560 // We never expect the following calls to iDBView methods to leave. |
|
561 TInt findResult=KErrNotFound; |
|
562 #ifdef _DEBUG |
|
563 TRAPD(err, |
|
564 iDBView->FirstL(); |
|
565 findResult = iDBView->FindL(RDbRowSet::EForwards, TDbQuery(aViewDescriptor)); |
|
566 ); |
|
567 __ASSERT_DEBUG(err == KErrNone, PanicServer(EBTManUnexpectedDbError)); |
|
568 #else |
|
569 iDBView->FirstL(); |
|
570 findResult = iDBView->FindL(RDbRowSet::EForwards, TDbQuery(aViewDescriptor)); |
|
571 #endif |
|
572 |
|
573 if (findResult != KErrNotFound) |
|
574 { |
|
575 match = ETrue; |
|
576 iSession.CompleteMessage(iViewChangeNotificationMessage, KErrNone); |
|
577 } |
|
578 |
|
579 #ifdef _DEBUG |
|
580 TRAP(err, iDBView->GotoL(bookmark)); |
|
581 __ASSERT_DEBUG(err == KErrNone, PanicServer(EBTManUnexpectedDbError)); |
|
582 #else |
|
583 iDBView->GotoL(bookmark); |
|
584 #endif |
|
585 } |
|
586 |
|
587 return match; |
|
588 } |
|
589 |
|
590 /*virtual*/ void CBTRegistrySubSession::SetViewChangeNotificationMessage(const RMessage2& aMessage) |
|
591 { |
|
592 LOG_FUNC |
|
593 if (iDBView) |
|
594 { |
|
595 iViewChangeNotificationMessage = aMessage; |
|
596 // Complete later |
|
597 } |
|
598 else |
|
599 { |
|
600 iSession.CompleteMessage(aMessage, KErrNotReady); |
|
601 } |
|
602 } |