bluetoothmgmt/btmgr/BTManServer/RegistrySubSession.cpp
author Pat Downey <patd@symbian.org>
Wed, 01 Sep 2010 12:38:54 +0100
branchRCL_3
changeset 24 e9b924a62a66
parent 0 29b1cd4cb562
permissions -rw-r--r--
Revert incorrect RCL_3 drop: Revision: 201031 Kit: 201035

// 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);
		}
	}