diff -r 000000000000 -r 29b1cd4cb562 bluetoothmgmt/btmgr/BTManServer/RegistrySubSession.cpp --- /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 + +#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(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 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(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); + } + }