--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetoothmgmt/btmgr/BTManServer/RegistrySubSession.cpp Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,602 @@
+// Copyright (c) 1999-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:
+// Provides the implementation of the subsession that refers to remote devices
+//
+//
+
+
+#include "BTManServer.h"
+#include "btmanserverutil.h"
+#include <bluetooth/logger.h>
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_BT_MANAGER_SERVER);
+#endif
+
+//=====================================================================
+// CBTRegistrySubSession
+//=====================================================================
+//Description: Provides an interface to the registry for device
+// This is needed to provide device connection information for
+// subsequent connections.
+//=====================================================================
+
+
+CBTRegistrySubSession* CBTRegistrySubSession::NewL(CBTManSession& aSession, CBTRegistry& aRegistry)
+ {
+ LOG_STATIC_FUNC
+ CBTRegistrySubSession* self = new(ELeave) CBTRegistrySubSession(aSession, aRegistry);
+ //Since its a CObject derived class so we should use CleanupClosePushL
+ CleanupClosePushL(*self);
+ self->ConstructL();
+ CleanupStack::Pop();
+ return self;
+ }
+
+void CBTRegistrySubSession::ConstructL()
+ {
+ LOG_FUNC
+ }
+
+CBTRegistrySubSession::CBTRegistrySubSession(CBTManSession& aSession, CBTRegistry& aRegistry)
+: CBTManSubSession(aSession,aRegistry)
+ {
+ LOG_FUNC
+ }
+
+CBTRegistrySubSession::~CBTRegistrySubSession()
+ {
+ LOG_FUNC
+ DoCloseView();
+ delete iResultBuffer;
+ }
+
+
+void CBTRegistrySubSession::OpenViewL(const TBTRegistrySearch& aSearch, const RMessage2& aMessage)
+/**
+ @param aSearch The structure containing the parameters to search for
+ @param aMessage The message to complete
+**/
+ {
+ LOG_FUNC
+ if (iDBView)
+ {
+ // previous view hasn't been retrieved
+ User::Leave(KErrInUse);
+ }
+
+ // Form the SQL query based on the search criteria
+ RBTDbQuery query;
+ CleanupClosePushL(query);
+ query.SearchL(aSearch);
+
+ // execute the SQL
+ iDBView = iRegistry.OpenViewL(query, iBookmark);
+
+ iUnderlyingSearch = query.ConstraintBuf().AllocL();
+
+ // see how many records are in the view
+ iViewCount = iDBView->CountL();
+
+ // send count back to client
+ if (!iViewCount)
+ {
+ User::Leave(KErrNotFound);
+ }
+ else
+ {
+ // go to the first record in the view for later retrieval
+ iDBView->FirstL();
+ iSession.CompleteMessage(aMessage, iViewCount);
+ }
+ CleanupStack::Pop(1); // query
+ // **leave view open for client's later requests upon it**
+ }
+
+void CBTRegistrySubSession::UnpairL(const TBTDevAddr& aAddress, const RMessage2& aMessage)
+/**
+ @param aAddress The address to unbond
+ @param aMessage The message to complete
+**/
+ {
+ LOG_FUNC
+ // Form the SQL query based on the device address
+ RBTDbQuery query;
+ CleanupClosePushL(query);
+ query.FindDeviceL(aAddress);
+
+ // Execute the SQL
+ RDbView* localView = iRegistry.OpenViewL(query, iBookmark);
+ CleanupCloseDeletePushL(localView);
+
+ HBufC* localViewUnderlyingSearch = query.ConstraintBuf().AllocLC();
+
+ TInt c = localView->CountL();
+
+ if (c)
+ {
+ // device is there - check to see if client is allowed to change
+ __ASSERT_DEBUG(c==1, PanicServer(EBTManTooManyDevicesInView));
+
+ // Try to notify interested parties before deleting the device.
+ // If we notify afterwards, then the device will fall out of someone's
+ // view and a change would then not be detected on that view.
+ NotifyChange(*this, *localViewUnderlyingSearch);
+
+ // the client is allowed to change the details
+ iRegistry.UnpairL(*localView);
+ iSession.CompleteMessage(aMessage, KErrNone);
+ }
+ else
+ {
+ // device wasn't even there
+ User::Leave(KErrNotFound);
+ }
+
+ // Cleanup localViewUnderlyingSearch, localView and query
+ CleanupStack::PopAndDestroy(3, &query);
+ }
+
+void CBTRegistrySubSession::GetDeviceL(const TBTNamelessDevice& aDevice, const RMessage2& aMessage)
+/**
+ Get a single nameless device from registry
+ @param aDevice The device to retrieve (only the address is used as a key at present)
+ @param aMessage The message to complete
+**/
+ {
+ LOG_FUNC
+ // open the device
+ TDbBookmark bookmark;
+ RDbView* localView = iRegistry.OpenDeviceL(aDevice.Address(), bookmark);
+ CleanupCloseDeletePushL(localView);
+
+ TInt localCount = localView->CountL();
+
+ if (localCount==0)
+ {
+ // the device wasn't there!
+ User::Leave(KErrNotFound);
+ }
+ else
+ {
+ __ASSERT_DEBUG(localCount==1, PanicServer(EBTManTooManyDevicesInView));
+
+ // Get the resulting device (we take ownership), stays on cleanupstack
+
+
+ const TBTNamelessDevice* device =
+ iRegistry.GetNextNamelessDeviceLC(*localView,bookmark,aMessage.HasCapability(ECapabilityReadDeviceData));
+
+
+ // Write back resulting device to client
+ TBTNamelessDevicePckgBuf writePckg;
+ writePckg = *device;
+
+ aMessage.WriteL(0, writePckg);
+ iSession.CompleteMessage(aMessage, KErrNone);
+
+ CleanupStack::Pop(1); // device
+ delete const_cast<TBTNamelessDevice*>(device); //MSDev workaround
+ }
+ CleanupStack::PopAndDestroy(localView);
+ }
+
+
+void CBTRegistrySubSession::AddDeviceL(const CBTDevice& aDetails, const RMessage2& aMessage)
+/**
+ @note This is an insertion, which can be slower via SQL; so we stick with C++ methods
+ Further, this saves us the effort of converting various binary values into the ASCII
+ SQL equivalent, only for DBMS to have to retokenise it ;-)
+ Slight disadvantage is that the C++ interface to DBMS is synchronous...
+ ..but the addition of a new row unordered should not be hard in our small database
+**/
+ {
+ LOG_FUNC
+
+ iRegistry.CreateDeviceL(aDetails,
+ aMessage.HasCapability(ECapabilityWriteDeviceData),
+ aMessage.SecureId());
+ // Form the SQL query based on the device address
+ RBTDbQuery query;
+ CleanupClosePushL(query);
+ query.FindDeviceL(aDetails.BDAddr());
+
+ HBufC* localViewUnderlyingSearch = query.ConstraintBuf().AllocLC();
+
+ iSession.CompleteMessage(aMessage, KErrNone);
+
+ // Try to notify interested parties after adding the device.
+ // We need the device to be present in other views for change notification to work.
+ NotifyChange(*this, *localViewUnderlyingSearch);
+
+ // Cleanup localViewUnderlyingSearch and query
+ CleanupStack::PopAndDestroy(2, &query);
+ }
+
+void CBTRegistrySubSession::ModifyL(const CBTDevice& aNewDetails, const RMessage2& aMessage)
+/**
+ @param aNewDetails The new device details; with the address to modify
+ @param aMessage The message to complete
+**/
+ {
+ LOG_FUNC
+ // check that we can modify this device...
+ if (!aNewDetails.IsValidBDAddr())
+ {
+ // the device address has not been set correctly
+ User::Leave(KErrCorrupt);
+ }
+
+ if (!iRegistry.DevicePresentL(aNewDetails.BDAddr()))
+ {
+ // the device record that the client wants to modify is not present in the registry
+ User::Leave(KErrNotFound);
+ }
+ else
+ {
+ // device is in Registry - open local device view
+ TDbBookmark dummy; // ignore position for now
+
+ // Form the SQL query based on the device address
+ RBTDbQuery query;
+ CleanupClosePushL(query);
+ query.FindDeviceL(aNewDetails.BDAddr());
+
+ // Execute the SQL
+ RDbView* localView = iRegistry.OpenViewL(query, dummy);
+ CleanupCloseDeletePushL(localView);
+
+ HBufC* localViewUnderlyingSearch = query.ConstraintBuf().AllocLC();
+
+ // Try to notify interested parties before making the change.
+ // If we notify afterwards, then the device may fall out of someone's
+ // view and a change would then not be detected on that view.
+ NotifyChange(*this, *localViewUnderlyingSearch);
+
+ iRegistry.UpdateDeviceL(*localView, aNewDetails);
+ iSession.CompleteMessage(aMessage, KErrNone);
+
+ // Cleanup localViewUnderlyingSearch, localView and query
+ CleanupStack::PopAndDestroy(3, &query);
+ }
+ }
+
+void CBTRegistrySubSession::ModifyL(const TBTNamelessDevice& aNewDetails, const RMessage2& aMessage)
+/**
+ @param aNewDetails The new device details; with the address to modify
+ @param aMessage The message to complete
+**/
+ {
+ LOG_FUNC
+ // make a CBTDevice based on the T class
+ CBTDevice* tempDevice = CBTDevice::NewLC(aNewDetails);
+ ModifyL(*tempDevice, aMessage);
+ CleanupStack::PopAndDestroy(tempDevice);
+ // Notification already handled by overloaded ModifyL() implementation
+ }
+
+void CBTRegistrySubSession::ModifyNameL(const TBTDevAddr& aAddress, const RMessage2& aMessage)
+/**
+ Updates one of the names for a device in the Registry
+ Here we decide if we can work with an address or not
+
+ @param aAddress The address of the device to modify
+ @param aMessage The message to complete, and from which we get the name
+**/
+ {
+ LOG_FUNC
+
+ // Form the SQL query based on the device address
+ RBTDbQuery query;
+ CleanupClosePushL(query);
+ query.FindDeviceL(aAddress);
+
+ // Execute the SQL
+ RDbView* localView = iRegistry.OpenViewL(query, iBookmark);
+ CleanupCloseDeletePushL(localView);
+
+ HBufC* localViewUnderlyingSearch = query.ConstraintBuf().AllocLC();
+
+ TInt c = localView->CountL();
+
+ if (!c)
+ {
+ // can't modify name cos device isn't there!
+ User::Leave(KErrNotFound);
+ }
+
+
+
+ // read the names
+ TBuf8<KMaxBluetoothNameLen> buf;
+ aMessage.ReadL(1, buf);
+
+ // Try to notify interested parties before making the change.
+ // If we notify afterwards, then the device may fall out of someone's
+ // view and a change would then not be detected on that view.
+ NotifyChange(*this, *localViewUnderlyingSearch);
+
+ // read out what's already in the database
+ iRegistry.UpdateNameL(aAddress, buf, static_cast<TBTManServerRequest>(aMessage.Function()));
+ // done, OK at this stage
+ iSession.CompleteMessage(aMessage, KErrNone);
+
+ // Cleanup localViewUnderlyingSearch, localView and query
+ CleanupStack::PopAndDestroy(3, &query);
+ }
+
+void CBTRegistrySubSession::PreLoadL(const RMessage2& aMessage)
+/**
+ Load in the results from DBMS
+ we'd have to load it all in at some time for giving to client, so we extend
+ the lifetime a bit. We kill the cache upon delivery to client.
+
+ This approach allows us to compute the size of the result set,
+ so that we can tell client
+
+ @param aMessage The message to complete
+**/
+ {
+ LOG_FUNC
+ if (!iViewCount)
+ {
+ // client has asked for something we dont have
+ User::Leave(KErrNotFound);
+ }
+
+ // In case a previous registry response is cancelled, and then for the next request,
+ // a new registry response is created, the iResultBuffer could still be non-zero.
+ // So, release memory now.
+ delete iResultBuffer;
+ iResultBuffer = NULL;
+
+ // Create a buffer to hold the externalised entry
+ iResultBuffer = CBufFlat::NewL(sizeof(CBTDevice)); // just a granularity for expansion
+
+ RBufWriteStream stream;
+ CleanupClosePushL(stream);
+ stream.Open(*iResultBuffer); // returns void
+
+ TInt res = KErrNone;
+ CBTDevice* device = NULL;
+
+ iNumDevicesInBuffer=0;
+ while (res == KErrNone)
+ {
+ // Get the entry and externalise to stream.
+ TRAP(res,
+ device= iRegistry.GetNextDeviceL(*iDBView,
+ iBookmark,
+ aMessage.HasCapability(ECapabilityReadDeviceData));
+ CleanupStack::PushL(device);
+ device->ExternalizeL(stream);
+ CleanupStack::PopAndDestroy(device);
+ device = NULL;
+ iNumDevicesInBuffer++;
+
+ );
+ }
+
+ // complete message with size of the blob or the error
+ TInt code;
+ if (res == KErrEof)
+ {
+ code = iResultBuffer->Size();
+ }
+ else
+ {
+ code = res;
+ }
+ iSession.CompleteMessage(aMessage, code);
+ CleanupStack::PopAndDestroy(1); // stream
+
+ // do not need to close view here
+ }
+
+
+void CBTRegistrySubSession::RetrieveL(const RMessage2& aMessage)
+/**
+ Returns the previously cached result set to the client
+
+ @param aMessage The message to complete
+**/
+ {
+ LOG_FUNC
+ if (!iResultBuffer)
+ {
+ // they haven't preloaded
+ User::Leave(KErrNotReady);
+ }
+ // write the buffer of devices back
+ aMessage.WriteL(0, iResultBuffer->Ptr(0));
+ iSession.CompleteMessage(aMessage, iNumDevicesInBuffer);
+
+ // release memory now to reduce footprint
+ delete iResultBuffer;
+ iResultBuffer = NULL;
+ }
+
+void CBTRegistrySubSession::DeleteViewL(const RMessage2& aMessage)
+/**
+ Deletes all devices in the view - could be used to delete many devices in one call
+
+ @param aMessage The message to complete
+**/
+ {
+ LOG_FUNC
+ if (!iViewCount)
+ {
+ // no point calling delete on a view with no devices contained!
+ User::Leave(KErrNotReady);
+ }
+
+ // Try to notify interested parties before deleting devices.
+ // If we notify afterwards, then the devices will fall out of someone's
+ // view and a change would then not be detected on that view.
+ NotifyChange(*this, *iUnderlyingSearch);
+
+ // Delete all in view only if client has W.D.D capability. Otherwise delete the
+ // devices whose SID value is same as the SecureId
+ iRegistry.DeleteViewL( *iDBView,
+ aMessage.HasCapability(ECapabilityWriteDeviceData),
+ aMessage.SecureId());
+
+
+ iSession.CompleteMessage(aMessage, KErrNone);
+
+ // The view is not null, but probably not of much
+ // use (ie. a view containing devices created by processes other than your own)
+ // In addition, making the closure conditional would mean a potential SC
+ // break for code moving from non-secure to secure world, so DoCloseView
+ // remains non-optional at this point.
+ DoCloseView();
+ }
+
+void CBTRegistrySubSession::UnpairViewL(const RMessage2& aMessage)
+/**
+ Unpairs all devices in the view
+
+ @param aMessage The message to complete
+**/
+ {
+ LOG_FUNC
+ if (!iViewCount)
+ {
+ // no point calling delete on a view with no devices contained!
+ User::Leave(KErrNotReady);
+ }
+
+ // Try to notify interested parties before making the change.
+ // If we notify afterwards, then the device may fall out of someone's
+ // view and a change would then not be detected on that view.
+ NotifyChange(*this, *iUnderlyingSearch);
+
+ iRegistry.UnpairViewL(*iDBView);
+ iSession.CompleteMessage(aMessage, KErrNone);
+ }
+
+void CBTRegistrySubSession::CloseView(const RMessage2& aMessage)
+/**
+ Client has finished with the view
+
+ @param aMessage The message to complete
+*/
+ {
+ LOG_FUNC
+ DoCloseView();
+ iSession.CompleteMessage(aMessage, KErrNone);
+
+ // no underlying registry change, so don't notify
+ }
+
+void CBTRegistrySubSession::DoCloseView()
+/**
+ Close the internal view if it exists
+**/
+ {
+ LOG_FUNC
+ if (iDBView)
+ {
+ iDBView->Close();
+ delete iDBView;
+ iDBView=NULL;
+
+ delete iUnderlyingSearch;
+ iUnderlyingSearch=NULL;
+ }
+ // No point having notify on closed view; yet session may have already processed a cancel on it
+ if (iSession.FindMessage(iViewChangeNotificationMessage))
+ {
+ iSession.CompleteMessage(iViewChangeNotificationMessage, KErrCompletion);
+ }
+
+ iViewCount=0;
+ }
+
+
+void CBTRegistrySubSession::Cleanup(TInt /*aError*/)
+ {
+ LOG_FUNC
+ // this subsession can help by closing the view
+ // it is unlikely to be in a good state anyway
+ DoCloseView();
+ }
+
+void CBTRegistrySubSession::NotifyChange(CBTManSubSession& aSubSessionViewOwner, const TDesC& aViewDescriptor)
+/**
+ Called by methods that know they've changed settings on the remotedevice table
+ This method will notify the client that was interested
+**/
+ {
+ LOG_FUNC
+ CBTManSubSession::NotifyChange(KRegistryChangeRemoteTable, aSubSessionViewOwner, aViewDescriptor);
+ }
+
+TBool CBTRegistrySubSession::IsOverlappingView(const TDesC& aViewDescriptor)
+ {
+ LOG_FUNC
+ // Need to take the view descriptor and test with our view
+ TBool match = EFalse;
+
+ // We cannot rely on the handle of iViewChangeNotificationMessage to provide an indicator
+ // of whether or not the message is completed, since we have a copy of the RMessage2 held by
+ // the CBTManMessage instance.
+ if (iDBView && iSession.FindMessage(iViewChangeNotificationMessage))
+ {
+ // Bookmark current place then use at end
+ TDbBookmark bookmark = iDBView->Bookmark();
+
+ // We never expect the following calls to iDBView methods to leave.
+ TInt findResult=KErrNotFound;
+#ifdef _DEBUG
+ TRAPD(err,
+ iDBView->FirstL();
+ findResult = iDBView->FindL(RDbRowSet::EForwards, TDbQuery(aViewDescriptor));
+ );
+ __ASSERT_DEBUG(err == KErrNone, PanicServer(EBTManUnexpectedDbError));
+#else
+ iDBView->FirstL();
+ findResult = iDBView->FindL(RDbRowSet::EForwards, TDbQuery(aViewDescriptor));
+#endif
+
+ if (findResult != KErrNotFound)
+ {
+ match = ETrue;
+ iSession.CompleteMessage(iViewChangeNotificationMessage, KErrNone);
+ }
+
+#ifdef _DEBUG
+ TRAP(err, iDBView->GotoL(bookmark));
+ __ASSERT_DEBUG(err == KErrNone, PanicServer(EBTManUnexpectedDbError));
+#else
+ iDBView->GotoL(bookmark);
+#endif
+ }
+
+ return match;
+ }
+
+/*virtual*/ void CBTRegistrySubSession::SetViewChangeNotificationMessage(const RMessage2& aMessage)
+ {
+ LOG_FUNC
+ if (iDBView)
+ {
+ iViewChangeNotificationMessage = aMessage;
+ // Complete later
+ }
+ else
+ {
+ iSession.CompleteMessage(aMessage, KErrNotReady);
+ }
+ }