bluetoothmgmt/btmgr/BTManServer/RegistrySubSession.cpp
changeset 0 29b1cd4cb562
--- /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);
+		}
+	}