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

// Copyright (c) 2010 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:
// Implements backup and restore support for the Bluetooth Manager server.
//

#include <e32std.h>
#include <e32base.h>
#include <e32property.h>
#include <f32file.h>
#include <s32file.h>
#include "btmanserverutil.h"
#include "btmanserverburmgr.h"
#include "BTManServer.h"

#include "btmanclient.h"
#include "btdevice.h"
#include <bluetooth/hci/hcitypes.h>
#include <bluetooth/logger.h>
#include "BTSec.h"

#ifdef __FLOG_ACTIVE
_LIT8(KLogComponent, LOG_COMPONENT_BT_MANAGER_SERVER);
#endif


/**
Raises an EBTBURMgrInvalidState panic with state information encoded within the panic code.
The first digit denotes the panic code (EBTBURMgrInvalidState).
The second digit is a delimiter and is always 1.
The next two digits represent the expected state
The next digit again is a delimiter and is always 1.
The final two digits represent the actual state. 

For example, the panic code 7101100 indicates an EBTBURMgrInvalidState panic raised because the state
machine is in the EBTBURStateNormal state when it was expected to be in the EBTBURStateBackupRequest state.
**/
void InvalidStatePanic(TBTBURState aExpectedState, TBTBURState aActualState)
	{
	LOG_STATIC_FUNC
	User::Panic(KBTBackupPanicCat, (EBTBURMgrInvalidState*KBTBackupStatePanicMultiplier) + KBTBackupStatePanicMajorDelimiter + (aExpectedState * KBTBackupStateMultiplier) + KBTBackupStatePanicMinorDelimiter + aActualState);
	}

/**
Raises an EBTBURMgrInvalidStateTransition panic with state information encoded within the panic code.
The first digit denotes the panic code (EBTBURMgrInvalidStateTransition).
The second digit is a delimiter and is always 1.
The next two digits represent the expected state
The next digit again is a delimiter and is always 1.
The final two digits represent the actual state. 

For example, the panic code 6101100 indicates an EBTBURMgrInvalidStateTransition panic raised because the state
machine is in the EBTBURStateNormal state when it was expected to be in the EBTBURStateBackupRequest state.
**/
void InvalidStateTransitionPanic(TBTBURState aCurrentState, TBTBUREvent aEvent)
	{
	LOG_STATIC_FUNC
	User::Panic(KBTBackupPanicCat, (EBTBURMgrInvalidStateTransition*KBTBackupStatePanicMultiplier) + KBTBackupStatePanicMajorDelimiter + (aCurrentState * KBTBackupStateMultiplier) + KBTBackupStatePanicMinorDelimiter + aEvent);
	}


/**
CBTManServerBURMgr - Manages backup and restore support for the Bluetooth Manager server.
**/

/**
Constructs a new instance of a CBTManServerBURMgr.
An MBTBURNotify instance may be registered here for notifications. Ownership of the MBTBURNotify instance is not transferred.
@see CBTManServerBURMgr::RequestBURNotification()
**/
CBTManServerBURMgr* CBTManServerBURMgr::NewL(CBTManServer& aBTManServer, MBTBURNotify* aBURNotify, TInt aPriority)
	{
	LOG_STATIC_FUNC
	CBTManServerBURMgr* self = new(ELeave) CBTManServerBURMgr(aBTManServer, aBURNotify, aPriority);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

CBTManServerBURMgr::CBTManServerBURMgr(CBTManServer& aBTManServer, MBTBURNotify* aBURNotify, TInt aPriority) 
	: CActive(aPriority), 
	iBTManServer(aBTManServer),  
	iBUROperationStartNotified(EFalse)
	{
	LOG_FUNC

	RequestBURNotification(aBURNotify);
	CActiveScheduler::Add(this);
	}

CBTManServerBURMgr::~CBTManServerBURMgr()
	{
	LOG_FUNC

	Cancel();
	iProperty.Close();

	// Close any open session on BT Manager
	NotifyAnyBUROperationStopped();
		
	// Members which either hold data or are NULL
	delete iActiveBackupClient;
	delete iActiveBackupDataClient;
	delete iBackupHandler;
	delete iRestoreHandler;
	delete iLocalAddrFetcher;

	// Member that is instantiated for entire lifetime of CBTManServerBURMgr
	delete iStateMachine;
	}

void CBTManServerBURMgr::ConstructL()
	{
	LOG_FUNC

	// Construct state machine
	iStateMachine = CBTManServerBURMgrStateFactory::NewL(*this);
	// Construction counts as a transition into the default state (EBTBURStateNormal) of the state machine.
	// However, since this does nothing if no backup/restore cleanup is required, we do not need to execute the 
	// state action.
	
	// Setup iProperty to catch BUR flag from Secure Backup Engine
	LEAVEIFERRORL(iProperty.Attach(KUidSystemCategory, KUidBackupRestoreKey));

	// Subscribe to BUR P&S key to catch transitions
	SubscribeToBackupRestoreKey();

	// Also check flag now (it may have already transitioned)
	TInt backupKeyValue = 0;
	LEAVEIFERRORL(iProperty.Get(backupKeyValue));
	TBURPartType backupState = GetBURPartType(backupKeyValue);	

	// If we're not about to perform a restore, check if a restore file exists. If so, try to process restore file.
	// The restore operation may not affect us, or may be postponed. In either case, we try to process the restore file
	// again next time we start.
	if (backupState != EBURRestoreFull && backupState != EBURRestorePartial)
		{
		// Delete any stale backup files
		DeleteBackupFile();
		// See if a restore file needs processing
		CheckForRestoreFileL();
		}

	// Process flag
	ProcessBackupState(backupState);
	}

/**
Translates a given integer obtained from the KUidBackupRestoreKey P&S key to a TBURPartType value.
This will validate the masked value to ensure that it falls within the range of the TBURPartType enum.
In debug builds, a panic will be raised if this value is out of range.
In release builds, EBURUnset is returned if the value is out of range.
**/
TBURPartType CBTManServerBURMgr::GetBURPartType(TInt aValue)
	{
	LOG_FUNC

	switch (aValue & KBURPartTypeMask)
		{
	case EBURUnset:
		return EBURUnset;
	case EBURNormal:
		return EBURNormal;
	case EBURBackupFull:
		return EBURBackupFull;
	case EBURBackupPartial:
		return EBURBackupPartial;
	case EBURRestoreFull:
		return EBURRestoreFull;
	case EBURRestorePartial:
		return EBURRestorePartial;
	default:
		__ASSERT_DEBUG(EFalse, PANIC(KBTBackupPanicCat, EBTBURMgrInvalidBackupRestoreState));
		return EBURUnset;
		}
	}

/**
Attempts to locate a restore file in the private directory of this server.
@return ETrue if the file can be found, EFalse otherwise.
**/
TBool CBTManServerBURMgr::RestoreFilePresent()
	{
	LOG_FUNC

	RFs fsSession;
	TInt err = fsSession.Connect();
	if (err == KErrNone)
		{
		err = fsSession.SetSessionToPrivate(fsSession.GetSystemDrive());
		if (err ==KErrNone)
			{
			TEntry entry;
			err = fsSession.Entry(KBTManServerRestoreFileName, entry);
			}

		fsSession.Close();
		}

	// If restore file was found, err will be KErrNone
	return (err == KErrNone);
	}

/**
Checks for the existence of a restore file. If a restore file is found, then the local device address
is feteched and processing of that restore file is initiated.
**/
void CBTManServerBURMgr::CheckForRestoreFileL()
	{
	LOG_FUNC

	if (RestoreFilePresent())
		{
		// Fetch local address. When this is received, restore file processing will begin.
		iLocalAddrFetcher = CBTLocalAddressFetcher::NewL(*this, iBTManServer.Registry());
		iLocalAddrFetcher->FetchLocalAddress();
		}
	}

/**
Subscribes to the P&S flag provided by the Secure Backup Engine (to receive notification of backup/restore state changes).
**/
void CBTManServerBURMgr::SubscribeToBackupRestoreKey()
	{
	LOG_FUNC

	iProperty.Subscribe(iStatus);
	SetActive();	
	}

/**
Processes a change in the backup/restore state of the device. 
@param aBackupStateValue A value expressing the backup/restore state of the device. 
**/
void CBTManServerBURMgr::ProcessBackupState(TBURPartType aBURStateValue)
	{
	LOG_FUNC

	iBURState = aBURStateValue;

	switch (aBURStateValue)
		{
	case EBURBackupFull:
		// Fall-through
	case EBURBackupPartial:
		iStateMachine->TransitionState(EBTBUREventBackup);
		break;
	case EBURRestoreFull:
		// Fall-through
	case EBURRestorePartial:
		iStateMachine->TransitionState(EBTBUREventRestore);
		break;
	case EBURNormal:
		iStateMachine->TransitionState(EBTBUREventNormal);
		break;
	default:	// EBURUnset
		__ASSERT_DEBUG(aBURStateValue == EBURUnset, PANIC(KBTBackupPanicCat, EBTBURMgrUnknownBUREvent));
		iStateMachine->TransitionState(EBTBUREventUnset);
		}
	}

/**
Registers an MBTBURNotify instance to receive notification of backup/restore operations starting and stopping.
Ownership of this MBTBURNotify instance is not transferred.
Any previous MBTBURNotify instance is deregistered on this method call.
Supplying a NULL pointer will deregister the previously registered instance (if present).
**/
void CBTManServerBURMgr::RequestBURNotification(MBTBURNotify* aBURNotify)
	{
	LOG_FUNC

	iBURNotify = aBURNotify;
	}


/**
Receives a local address from a CBTLocalAddressFetcher instance.
This will trigger processing of a pending restore file.
**/
void CBTManServerBURMgr::SetLocalAddress(TBTDevAddr& aLocalAddr)
	{
	LOG_FUNC
	__ASSERT_DEBUG(aLocalAddr != TBTDevAddr(), PANIC(KBTBackupPanicCat, EBTBURMgrMissingLocalAddress));

	// Use address only if it is non-zero.
	if (aLocalAddr != TBTDevAddr())
		{
		// Take a copy
		iLocalAddr = aLocalAddr;
		// Attempt to start restore file processing.
		// Note that this will fail if we are not in the normal state.
		iStateMachine->TransitionState(EBTBUREventProcessRestoreFile);
		}
	}

/**
Receives notification that a restore file has been provided by the Secure Backup Engine.
Upon receiving this notification, the restore file is renamed to have the appropriate extension 
The actual restore to the registry is postponed until the next time BTManServer starts, 
so any ongoing BT connections are not affected.
**/
void CBTManServerBURMgr::RestoreFileReady()
	{
	LOG_FUNC
	__ASSERT_DEBUG(iStateMachine->GetCurrentState() == EBTBURStateRestoreOngoing, InvalidStatePanic(EBTBURStateRestoreOngoing, iStateMachine->GetCurrentState()));

	// Rename restore file
	RenameBackupFileForRestore();
	}

void CBTManServerBURMgr::HandleStateNormal()
	{
	LOG_FUNC
	__ASSERT_DEBUG(iStateMachine->GetCurrentState() == EBTBURStateNormal, InvalidStatePanic(EBTBURStateNormal, iStateMachine->GetCurrentState()));

	if (iActiveBackupClient)
		{
		// Backup or restore finished
		if (iBackupHandler)
			{
			// Backup operation finished
			// Delete backup file and destroy backup handler
			DeleteBackupFile();
			delete iBackupHandler;
			iBackupHandler = NULL;
			}
		else
			{
			// Restore operation finished
			delete iActiveBackupDataClient;
			iActiveBackupDataClient = NULL;
			}

		// Destroy active backup client
		delete iActiveBackupClient;
		iActiveBackupClient = NULL;

		// Notify that backup/restore operation has finished
		NotifyBUROperationStopped();
		}
	}

/**
Determines if a BUR operation affects the Bluetooth Manager server.
@return ETrue if a full backup or restore is ongoing, otherwise the return value
given by CActiveBackupClient::DoesPartialBURAffectMeL()
**/
TBool CBTManServerBURMgr::DoesBURAffectMeL(CActiveBackupClient& aClient)
	{
	LOG_FUNC

	if (iBURState == EBURBackupFull || iBURState == EBURRestoreFull)
		{
		return ETrue;
		}
	else
		{
		return aClient.DoesPartialBURAffectMeL();
		}
	}

void CBTManServerBURMgr::HandleStateBackupRequestL()
	{
	LOG_FUNC
	__ASSERT_DEBUG(iStateMachine->GetCurrentState() == EBTBURStateBackupRequest, InvalidStatePanic(EBTBURStateBackupRequest, iStateMachine->GetCurrentState()));

	// Determine if this backup operation affects us and transition to next state based on this outcome.
	iActiveBackupClient = CActiveBackupClient::NewL();

	if (DoesBURAffectMeL(*iActiveBackupClient))
		{
		// We're affected, proceed with backup
		iStateMachine->TransitionState(EBTBUREventBackupProceed);
		}
	else
		{
		// We're not effected, don't do any backup handling
		iStateMachine->TransitionState(EBTBUREventBackupReject);
		}
	}

void CBTManServerBURMgr::HandleStateBackupRequestError()
	{
	LOG_FUNC
	__ASSERT_DEBUG(iStateMachine->GetCurrentState() == EBTBURStateBackupRequest, InvalidStatePanic(EBTBURStateBackupRequest, iStateMachine->GetCurrentState()));

	// Transition to normal state
	iStateMachine->TransitionState(EBTBUREventAbortStateAction);
	}

void CBTManServerBURMgr::HandleStateBackupOngoingL()
	{
	LOG_FUNC
	__ASSERT_DEBUG(iStateMachine->GetCurrentState() == EBTBURStateBackupOngoing, InvalidStatePanic(EBTBURStateBackupOngoing, iStateMachine->GetCurrentState()));
	__ASSERT_DEBUG(iActiveBackupClient, PANIC(KBTBackupPanicCat, EBTBURMgrActiveBackupClientNull));

	// Provide notification that a BUR operation has started.
	NotifyBUROperationStarted();

	// Construct backup handler and prepare for backup
	iBackupHandler = CBTBackupHandler::NewL(*this, iBTManServer.Registry());
	iBackupHandler->CreateBackupFileL();

	// Signal that we're ready for (passive) backup of prepared file
	iActiveBackupClient->ConfirmReadyForBURL(KErrNone);

	// Next state transition will be invoked when BUR P&S key changes
	}

void CBTManServerBURMgr::HandleStateBackupOngoingError()
	{
	LOG_FUNC
	__ASSERT_DEBUG(iStateMachine->GetCurrentState() == EBTBURStateBackupOngoing, InvalidStatePanic(EBTBURStateBackupOngoing, iStateMachine->GetCurrentState()));

	// Transition to normal state
	iStateMachine->TransitionState(EBTBUREventAbortStateAction);
	}

void CBTManServerBURMgr::HandleStateBackupIgnore()
	{
	LOG_FUNC
	__ASSERT_DEBUG(iStateMachine->GetCurrentState() == EBTBURStateBackupIgnore, InvalidStatePanic(EBTBURStateBackupIgnore, iStateMachine->GetCurrentState()));

	// Destroy active backup client
	delete iActiveBackupClient;
	iActiveBackupClient = NULL;

	// Next state transition will be invoked when BUR P&S key changes
	}

void CBTManServerBURMgr::HandleStateRestoreRequestL()
	{
	LOG_FUNC
	__ASSERT_DEBUG(iStateMachine->GetCurrentState() == EBTBURStateRestoreRequest, InvalidStatePanic(EBTBURStateRestoreRequest, iStateMachine->GetCurrentState()));

	// We do want a callback here. Even though the actual restore operation has been handled passively, we
	// still need notificaton that the passive restore has completed (so we can then deal with the file).
	iActiveBackupDataClient = CBTActiveBackupDataClient::NewL(*this);
	iActiveBackupClient = CActiveBackupClient::NewL(iActiveBackupDataClient);

	// Determine if this restore operation affects us and transition to next state based on this outcome
	if (DoesBURAffectMeL(*iActiveBackupClient))
		{
		// We're affected, proceed with restore
		iStateMachine->TransitionState(EBTBUREventRestoreProceed);
		}
	else
		{
		// We're not effected, don't do any restore handling
		iStateMachine->TransitionState(EBTBUREventRestoreReject);
		}
	}

void CBTManServerBURMgr::HandleStateRestoreRequestError()
	{
	LOG_FUNC
	__ASSERT_DEBUG(iStateMachine->GetCurrentState() == EBTBURStateRestoreRequest, InvalidStatePanic(EBTBURStateRestoreRequest, iStateMachine->GetCurrentState()));
	
	// Transition to normal state
	iStateMachine->TransitionState(EBTBUREventAbortStateAction);
	}

void CBTManServerBURMgr::HandleStateRestoreOngoingL()
	{
	LOG_FUNC
	__ASSERT_DEBUG(iStateMachine->GetCurrentState() == EBTBURStateRestoreOngoing, InvalidStatePanic(EBTBURStateRestoreOngoing, iStateMachine->GetCurrentState()));

	// Cancel any local address requests
	if (iLocalAddrFetcher)
		{
		iLocalAddrFetcher->Cancel();
		}

	// Provide notification that a BUR operation has started.
	NotifyBUROperationStarted();

	// Signal that we're ready for active restore of prepared file
	// (note that no data is actively restored, but we still need to invoke this method)
	iActiveBackupClient->ConfirmReadyForBURL(KErrNone);

	// Next state transition will be invoked when BUR P&S key changes
	}

void CBTManServerBURMgr::HandleStateRestoreOngoingError()
	{
	LOG_FUNC
	__ASSERT_DEBUG(iStateMachine->GetCurrentState() == EBTBURStateRestoreOngoing, InvalidStatePanic(EBTBURStateRestoreOngoing, iStateMachine->GetCurrentState()));

	// Transition to normal state
	iStateMachine->TransitionState(EBTBUREventAbortStateAction);
	}

void CBTManServerBURMgr::HandleStateRestoreIgnore()
	{
	LOG_FUNC
	__ASSERT_DEBUG(iStateMachine->GetCurrentState() == EBTBURStateRestoreIgnore, InvalidStatePanic(EBTBURStateRestoreIgnore, iStateMachine->GetCurrentState()));

	// Destroy active backup client
	delete iActiveBackupClient;
	delete iActiveBackupDataClient;
	iActiveBackupClient = NULL;
	iActiveBackupDataClient = NULL;

	// Next state transition will be invoked when BUR P&S key changes
	}

void CBTManServerBURMgr::HandleStateProcessRestoreFileL()
	{
	LOG_FUNC
	__ASSERT_DEBUG(iStateMachine->GetCurrentState() == EBTBURStateProcessRestoreFile, InvalidStatePanic(EBTBURStateProcessRestoreFile, iStateMachine->GetCurrentState()));

	// Start restore file processing
	iRestoreHandler = CBTRestoreHandler::NewL(*this, iBTManServer);
	iRestoreHandler->RestoreRegistryL(iLocalAddr);

	iStateMachine->TransitionState(EBTBUREventProcessRestoreFileComplete);
	}

void CBTManServerBURMgr::HandleStateProcessRestoreFileError(TInt aError)
	{
	LOG_FUNC
	__ASSERT_DEBUG(iStateMachine->GetCurrentState() == EBTBURStateProcessRestoreFile, InvalidStatePanic(EBTBURStateProcessRestoreFile, iStateMachine->GetCurrentState()));

	// Delete restore handler
	delete iRestoreHandler;
	iRestoreHandler = NULL;
	
	// If aError is anything other than KErrNoMemory, then delete restore file.
	// OOM errors may be temporary, and a subsequent restore attempt may succeed.
	if (aError != KErrNoMemory)
		{
		DeleteRestoreFile();
		}

	// Transition to normal state
	iStateMachine->TransitionState(EBTBUREventAbortStateAction);
	}

void CBTManServerBURMgr::HandleStateRestoreFileProcessingComplete()
	{
	LOG_FUNC
	__ASSERT_DEBUG(iStateMachine->GetCurrentState() == EBTBURStateRestoreFileProcessingComplete, InvalidStatePanic(EBTBURStateRestoreFileProcessingComplete, iStateMachine->GetCurrentState()));
	
	// Delete restore file (we're now done with it)
	DeleteRestoreFile();

	// Delete restore handler
	delete iRestoreHandler;
	iRestoreHandler = NULL;

	iStateMachine->TransitionState(EBTBUREventRestoreFileTransitionNormal);
	}

/**
Sends notification to a registered MBTBURNotify instance that a backup or restore operation has started.
@see CBTManServerBURMgr::RequestBURNotification()
**/
void CBTManServerBURMgr::NotifyBUROperationStarted()
	{
	LOG_FUNC
	__ASSERT_DEBUG(!iBUROperationStartNotified, PANIC(KBTBackupPanicCat, EBTBURMgrBUROperationStartAlreadyNotified));
	
	if (!iBUROperationStartNotified)
		{
		iBUROperationStartNotified = ETrue;

		if (iBURNotify)
			{
			iBURNotify->BUROperationStarted();
			}
		}
	}

/**
Sends notification to the MBTBURNotify instance that a backup or restore operation has started. 
This should correspond to a call to NotifyBUROperationStarted().
@see CBTManServerBURMgr::RequestBURNotification()
**/
void CBTManServerBURMgr::NotifyBUROperationStopped()
	{
	LOG_FUNC
	__ASSERT_DEBUG(iBUROperationStartNotified, PANIC(KBTBackupPanicCat, EBTBURMgrBUROperationStartNotNotified));
	
	if (iBUROperationStartNotified)
		{
		iBUROperationStartNotified = EFalse;

		if (iBURNotify)
			{
			iBURNotify->BUROperationStopped();
			}
		}
	}

/**
Sends notification to the MBTBURNotify instance that a backup or restore operation has started. 
This will send notification only if NotifyBUROperationStarted() has been called without a corresponding 
call to NotifyBUROperationStopped().
This is intended for use in error situations only.
**/
void CBTManServerBURMgr::NotifyAnyBUROperationStopped()
	{
	LOG_FUNC

	// We deliberately want to fail silently here if no stop notification is expected.
	if (iBUROperationStartNotified)
		{
		iBUROperationStartNotified = EFalse;

		if (iBURNotify)
			{
			iBURNotify->BUROperationStopped();
			}
		}
	}

/**
Utility function to delete a named file from the private directory of this process on the system drive.
This is a best-efforts attempt which fails silently on error.
@param aFileName The name of the file to delete.
**/
void CBTManServerBURMgr::DeleteFile(const TDesC& aFileName)
	{
	LOG_FUNC

	RFs fsSession;
	// Attempt to delete file
	// This is best-efforts, failing silently on any error (as there is no remedy action that we can take).
	if (fsSession.Connect() == KErrNone)
		{
		if (fsSession.SetSessionToPrivate(fsSession.GetSystemDrive()) == KErrNone)
			{
			// Try to ensure read-only and system attributes are not set
			TUint attrs;
			TInt err = fsSession.Att(aFileName, attrs);
			if (err == KErrNone)
				{
				// Unset the files read-only and system attributes if these are set.
				TUint unsetAttrs = (attrs & KEntryAttReadOnly) | (attrs & KEntryAttSystem);
				if (unsetAttrs != KEntryAttNormal)
					{
					err = fsSession.SetAtt(aFileName, KEntryAttNormal, unsetAttrs);
					}

				// Attempt to delete the file, if we've managed to successfully retrieve and reset the file attributes. 
				// Note that this is best-efforts.
				if (err == KErrNone)
					{
					static_cast<void>(fsSession.Delete(aFileName));
					}
				}
			}

		fsSession.Close();
		}
	}	

/**
Deletes the backup file (with name KBTManServerBackupFileName) from the private directory of this process on the system drive.
This is a best-efforts attempt which fails silently on error.
**/
void CBTManServerBURMgr::DeleteBackupFile()
	{
	LOG_FUNC

	DeleteFile(KBTManServerBackupFileName);
	}

/**
Deletes the restore file (with name KBTManServerRestoreFileName) from the private directory of this process on the system drive.
This is a best-efforts attempt which fails silently on error.
**/
void CBTManServerBURMgr::DeleteRestoreFile()
	{
	LOG_FUNC

	DeleteFile(KBTManServerRestoreFileName);
	}

/**
Renames a restored backup file from KBTManServerBackupFileName to KBTManServerRestoreFileName.
This facilitates processing of the restore file when the Bluetooth Manager server next starts.
This is a best-efforts attempt, as no remedial action can be taken if this operation fails.
**/
void CBTManServerBURMgr::RenameBackupFileForRestore()
	{
	LOG_FUNC

	// Delete any previous restore file
	DeleteRestoreFile();
	
	RFs fsSession;
	if (fsSession.Connect() == KErrNone)
		{
		static_cast<void>(fsSession.Rename(KBTManServerBackupFileName, KBTManServerRestoreFileName));
		fsSession.Close();
		}
	}

void CBTManServerBURMgr::RunL()
	{
	LOG_FUNC
	
	if (iStatus == KErrNone)
		{
		// Subscribe to catch the next transition
		SubscribeToBackupRestoreKey();

		// Handle this event
		TInt backupKeyValue = 0;
		LEAVEIFERRORL(iProperty.Get(backupKeyValue));
		TBURPartType backupState = GetBURPartType(backupKeyValue);

		ProcessBackupState(backupState);
		}
	}

TInt CBTManServerBURMgr::RunError(TInt /*aError*/)
	{
	LOG_FUNC

	// Problem occured in obtaining backup key value.
	// Ignore the error, as there is nothing we can do.
	return KErrNone;
	}

void CBTManServerBURMgr::DoCancel()
	{
	LOG_FUNC
	
	// Cancel our subscription
	iProperty.Cancel();
	}

/**
 CBTBackupHandler - Handles the task of backing up the BT registry into a backup file ready for passive backup.
 **/
CBTBackupHandler* CBTBackupHandler::NewL(CBTManServerBURMgr& aBURMgr, CBTRegistry& aRegistry)
	{
	LOG_STATIC_FUNC

	CBTBackupHandler* result = new(ELeave) CBTBackupHandler(aBURMgr, aRegistry);
	CleanupStack::PushL(result);
	result->ConstructL();
	CleanupStack::Pop(result);
	return result;
	}

CBTBackupHandler::CBTBackupHandler(CBTManServerBURMgr& aBURMgr, CBTRegistry& aRegistry) 
	: iBURMgr(aBURMgr), 
	iRegistry(aRegistry)
	{
	LOG_FUNC
	}

void CBTBackupHandler::ConstructL()
	{
	LOG_FUNC

	iRegistryData=CBTRegistryBURData::NewL();
	}

CBTBackupHandler::~CBTBackupHandler()
	{
	LOG_FUNC

	delete iRegistryData;
	}

void CBTBackupHandler::CreateBackupFileL()
	{
	LOG_FUNC

	// Collect backup registry data
	iRegistryData->ReadFromRegistryL(iRegistry);
	
	// Only continue if the registry is populated (i.e. it may be in its default state, which does not need to be backed up).
	// Determine this by examining the local device address stored in the registry. The default registry has this set to 0x0.
	if (iRegistryData->IsLocalAddressNonZeroL())
		{
		// Construct backup file in private directory.
		RFs fsSession;
		LEAVEIFERRORL(fsSession.Connect());
		CleanupClosePushL(fsSession);
		LEAVEIFERRORL(fsSession.SetSessionToPrivate(fsSession.GetSystemDrive()));
		// Create private path if it does not already exist.
		LEAVEIFERRORL(fsSession.CreatePrivatePath(fsSession.GetSystemDrive()));

		RFileWriteStream fStream;
		// Create backup file, overwriting if necessary (don't care about previous backup files).
		LEAVEIFERRORL(fStream.Replace(fsSession, KBTManServerBackupFileName, EFileWrite));
		fStream.PushL();
			    
		// Write out data
		iRegistryData->WriteToStreamL(fStream);
		fStream.CommitL();

		CleanupStack::PopAndDestroy(2, &fsSession);
		}
	}

/**
 CBTRestoreHandler - Handles the task of restoring the BT registry from a restore file given by passive restore.
 **/
CBTRestoreHandler* CBTRestoreHandler::NewL(CBTManServerBURMgr& aBURMgr, CBTManServer& aManServer)
	{
	LOG_STATIC_FUNC

	CBTRestoreHandler* result = new(ELeave) CBTRestoreHandler(aBURMgr, aManServer);
	CleanupStack::PushL(result);
	result->ConstructL();
	CleanupStack::Pop(result);
	return result;
	}

CBTRestoreHandler::CBTRestoreHandler(CBTManServerBURMgr& aBURMgr, CBTManServer& aManServer) 
	: iBURMgr(aBURMgr), 
	iManServer(aManServer)
	{
	LOG_FUNC
	}
	
void CBTRestoreHandler::ConstructL()
	{
	LOG_FUNC

	iRegistryData = CBTRegistryBURData::NewL();
	}

CBTRestoreHandler::~CBTRestoreHandler()
	{
	LOG_FUNC

	delete iRegistryData;
	}

void CBTRestoreHandler::RestoreRegistryL(TBTDevAddr& aLocalAddr)
	{
	LOG_FUNC
	__ASSERT_DEBUG(aLocalAddr != TBTDevAddr(), PANIC(KBTBackupPanicCat, EBTBURMgrMissingLocalAddress));

	LoadRestoreDataL();
    
	// Compare local address held in restore file with our local address
	if (iRegistryData->IsLocalAddressEqualL(aLocalAddr))
		{
		// Proceed with restore
		CBTRegistry& registry = iManServer.Registry();

		if (iRegistryData->WriteLocalDeviceNameToRegistryL(registry))
			{
			NotifyLocalTableChange();
			NotifyLocalDeviceNameChange(iRegistryData->GetLocalDeviceNameL());
			}

		TInt noRemoteDevices = iRegistryData->CountRemoteDevicesL();
		for (TInt i = 0; i < noRemoteDevices; i++)
			{
			if (iRegistryData->WriteRemoteDeviceToRegistryL(registry, i))
				{
				NotifyRemoteTableChangeL(iRegistryData->GetRemoteDeviceL(i).BDAddr());
				}
			}
		}
	}

void CBTRestoreHandler::LoadRestoreDataL()
	{
	LOG_FUNC

	// Read restore file
	RFs fsSession;
	LEAVEIFERRORL(fsSession.Connect());
	CleanupClosePushL(fsSession);
	LEAVEIFERRORL(fsSession.SetSessionToPrivate(fsSession.GetSystemDrive()));
	// Assuming private directory exists at this point
	
	RFileReadStream fStream;
	LEAVEIFERRORL(fStream.Open(fsSession, KBTManServerRestoreFileName, EFileRead));
	fStream.PushL();

	// Read in data
	iRegistryData->ReadFromStreamL(fStream);

	// Cleanup
	CleanupStack::PopAndDestroy(2, &fsSession);	//fStream and fsSession
	}

/**
Sends a notification that the persistence table has been changed.
The notification is observable through the P&S key KPropertyKeyBluetoothGetRegistryTableChange. 
**/
void CBTRestoreHandler::NotifyLocalTableChange()
	{
	LOG_FUNC
	
	// Notify the P&S key that the remote devices table has changed
	iManServer.Publish(KPropertyKeyBluetoothGetRegistryTableChange, KRegistryChangeLocalTable);
	}

/**
Sends a notification that the local device name has been changed.
This notification is observable through the P&S key KPRopertyKeyBluetoothSetDeviceName.
@param aLocalName The modified local device name as an 8-bit descriptor.
**/
void CBTRestoreHandler::NotifyLocalDeviceNameChange(const TDesC8& aLocalName)
	{
	LOG_FUNC
	
	// The P&S key requires the local device name in unicode format.
	TBuf16<KMaxBluetoothNameLen> localNameUniCode;
	localNameUniCode.Copy(aLocalName);
	
	NotifyLocalDeviceNameChange(localNameUniCode);
	}

/**
Sends a notification that the local device name has been changed.
This notification is observable through the P&S key KPRopertyKeyBluetoothSetDeviceName.
@param aLocalName The modified local device name in unicode format.
**/
void CBTRestoreHandler::NotifyLocalDeviceNameChange(const TDesC16& aLocalName)
	{
	LOG_FUNC
	
	// The KPropertyKeyBluetoothSetDeviceName P&S key may or may not exist at this point.
	TInt err = RProperty::Set( KPropertyUidBluetoothCategory, KPropertyKeyBluetoothSetDeviceName, aLocalName);
	if (err != KErrNone && err != KErrNotFound)
		{
		LOG1(_L("CBTRegistryBURData::NotifyLocalDeviceNameChange() - RProperty::Set() failed with %d"), err);
		}
	}

/**
Sends a notification that a device in the remote devices table has been changed.
The notification is observable through the P&S key KPropertyKeyBluetoothGetRegistryTableChange. 
Interested parties can also use RBTRegistry::NotifyViewChange() to detect if the change affects their view.
**/
void CBTRestoreHandler::NotifyRemoteTableChangeL(const TBTDevAddr& aAddress)
	{
	LOG_FUNC
	
	// Construct SQL constraint which selects the device
	RBTDbQuery query;
	CleanupClosePushL(query);
	query.FindDeviceL(aAddress);

	HBufC* conDes = query.ConstraintBuf().AllocLC();

	// Notify the P&S key that the remote devices table has changed
	iManServer.Publish(KPropertyKeyBluetoothGetRegistryTableChange, KRegistryChangeRemoteTable);

	// Signal notifiers waiting on view change
	iManServer.NotifyViewChange(*conDes);

	// Cleanup constDes and query
	CleanupStack::PopAndDestroy(2, &query);
	}

/**
CBTRegistryBURData - manages data from the Bluetooth registry that is to be backed up or restored.
**/
CBTRegistryBURData* CBTRegistryBURData::NewL()
	{
	LOG_STATIC_FUNC

	CBTRegistryBURData* self = new(ELeave) CBTRegistryBURData();
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

CBTRegistryBURData::CBTRegistryBURData() 
	: iHasRegistryData(EFalse)
	{
	LOG_FUNC
	}

CBTRegistryBURData::~CBTRegistryBURData()
	{
	LOG_FUNC

	if (iRemoteDevices)	// Dont use ClearRegistryData() here as leave may have occured in ReadFromRegistryL() or ReadFromStreamL()
		{
		// Destroy all CBTDevices in iRemoteDevices (necessary before array deletion for CArrayPtrFlat)
		iRemoteDevices->ResetAndDestroy();
		}

	delete iLocalDevice;
	delete iRemoteDevicesSid;
	delete iRemoteDevices;
	}

void CBTRegistryBURData::ConstructL()
	{
	LOG_FUNC

	iRemoteDevicesSid = new (ELeave) CArrayFixFlat<TSecureId>(1);
	iRemoteDevices = new(ELeave) CBTDeviceArray(1);
	}
	
/**
Writes the registry data held in this instance to a given stream.
This does not write those registry fields which contain unset values.
@param aStream The stream to write the registry data to.
**/
void CBTRegistryBURData::WriteToStreamL(RWriteStream& aStream) const
	{
	LOG_FUNC

	// Writes registry data out to a given stream, avoiding any use of class externalisation.
	if (HasRegistryData())
		{
		// This follows a specific file-format (tied to the registry version information), which is documented seperately.

		// We should already have the following information for the local device.
		__ASSERT_DEBUG(iLocalDevice->IsValidAddress(), PANIC(KBTBackupPanicCat, EBTBURMgrExpectedDataMissing));

		// Write out version information
		aStream.WriteUint32L(iRegistryVersionMajor);
		aStream.WriteUint32L(iRegistryVersionMinor);

		// Persistence table:
		// Local device address and name only (both are mandatory).
		aStream.WriteL(iLocalDevice->Address().Des(), KBTDevAddrSize);

		__ASSERT_DEBUG(iLocalDevice->DeviceName().Length() <= KMaxTUint8, PANIC(KBTBackupPanicCat, EBTBURMgrDescriptorLengthTooLong));
		aStream.WriteUint8L(iLocalDevice->DeviceName().Length());
		aStream.WriteL(iLocalDevice->DeviceName(), iLocalDevice->DeviceName().Length());

		// Remote devices table
		TInt rdCount=CountRemoteDevicesL();

		aStream.WriteUint32L(rdCount);
		for (TInt i = 0; i < rdCount; i++)
			{
			const CBTDevice& nextRemDev = GetRemoteDeviceL(i);
			const TBTNamelessDevice& nextRemDevNameless = nextRemDev.AsNamelessDevice();

			__ASSERT_DEBUG(nextRemDev.IsValidBDAddr(), PANIC(KBTBackupPanicCat, EBTBURMgrExpectedDataMissing));

			// Work out what is set and derive bitmask (can use TBTDeviceSet here as is public)
			TUint32 rdSetMask = TBTNamelessDevice::EAddress;
			if (nextRemDev.IsValidDeviceClass())
				{
				rdSetMask |= TBTNamelessDevice::EDeviceClass;
				}
			if (nextRemDev.IsValidLinkKey())
				{
				rdSetMask |= TBTNamelessDevice::ELinkKey;
				}
			if (nextRemDev.IsValidGlobalSecurity())
				{
				rdSetMask |= TBTNamelessDevice::EGlobalSecurity;
				}
			if (nextRemDevNameless.IsValidPageScanRepMode())
				{
				rdSetMask |= TBTNamelessDevice::EPageScanRepMode;
				}
			if (nextRemDevNameless.IsValidPageScanMode())
				{
				rdSetMask |= TBTNamelessDevice::EPageScanMode;
				}
			if (nextRemDevNameless.IsValidPageScanPeriodMode())
				{
				rdSetMask |= TBTNamelessDevice::EPageScanPeriodMode;
				}
			if (nextRemDevNameless.IsValidClockOffset())
				{
				rdSetMask |= TBTNamelessDevice::EClockOffset;
				}
			if (nextRemDev.IsValidUsed())
				{
				rdSetMask |= TBTNamelessDevice::EUsed;
				}
			if (nextRemDev.IsValidSeen())
				{
				rdSetMask |= TBTNamelessDevice::ESeen;
				}
			if (nextRemDev.IsValidPassKey())
				{
				rdSetMask |= TBTNamelessDevice::EPassKey;
				}
			if (nextRemDev.IsValidUiCookie())
				{
				rdSetMask |= TBTNamelessDevice::EUiCookie;
				}
			if (nextRemDev.IsValidDeviceName())
				{
				rdSetMask |= CBTDevice::EDeviceName;
				}
			if (nextRemDev.IsValidFriendlyName())
				{
				rdSetMask |= CBTDevice::EFriendlyName;
				}
			// Now write out bitmask
			aStream.WriteUint32L(rdSetMask);

			// Write valid setting for next remote device
			aStream.WriteUint32L(GetRemoteDeviceEntrySidL(i));
			aStream.WriteL(nextRemDev.BDAddr().Des(), KBTDevAddrSize);
			if (rdSetMask & TBTNamelessDevice::EDeviceClass)
				{
				aStream.WriteUint32L(nextRemDev.DeviceClass().DeviceClass());
				}
			if (rdSetMask & TBTNamelessDevice::ELinkKey)
				{
				const TBTLinkKey& nextRemDevLinkKey = nextRemDev.LinkKey();
				aStream.WriteL(nextRemDevLinkKey, KHCILinkKeySize);
				aStream.WriteUint8L(nextRemDev.LinkKeyType());
				}
			if (rdSetMask & TBTNamelessDevice::EGlobalSecurity)
				{
				TBTDeviceSecurity nextRemDevGlobSec = nextRemDev.GlobalSecurity();
				aStream.WriteUint8L(nextRemDevGlobSec.SecurityValue());
				aStream.WriteUint32L(nextRemDevGlobSec.PasskeyMinLength());
				}
			if (rdSetMask & TBTNamelessDevice::EPageScanRepMode)
				{
				aStream.WriteUint8L(nextRemDevNameless.PageScanRepMode());
				}
			if (rdSetMask & TBTNamelessDevice::EPageScanMode)
				{
				aStream.WriteUint8L(nextRemDevNameless.PageScanMode());
				}
			if (rdSetMask & TBTNamelessDevice::EPageScanPeriodMode)
				{
				aStream.WriteUint8L(nextRemDevNameless.PageScanPeriodMode());
				}
			if (rdSetMask & TBTNamelessDevice::EClockOffset)
				{
				aStream.WriteUint16L(nextRemDevNameless.ClockOffset());
				}
			if (rdSetMask & TBTNamelessDevice::EUsed)
				{
				const TInt64& used = nextRemDev.Used().Int64();
				aStream.WriteInt32L(I64HIGH(used));
				aStream.WriteInt32L(I64LOW(used));
				}
			if (rdSetMask & TBTNamelessDevice::ESeen)
				{
				const TInt64& seen = nextRemDev.Seen().Int64();
				aStream.WriteInt32L(I64HIGH(seen));
				aStream.WriteInt32L(I64LOW(seen));
				}
			if (rdSetMask & TBTNamelessDevice::EPassKey)
				{
				const TBTPinCode& nextRemDevPassKey = nextRemDev.PassKey();
				__ASSERT_DEBUG(nextRemDevPassKey.Length() == KHCIPINCodeSize + 1, PANIC(KBTBackupPanicCat, EBTBURMgrDescriptorUnexpectedLength));
				aStream.WriteL(nextRemDevPassKey, KHCIPINCodeSize + 1);
				}
			if (rdSetMask & TBTNamelessDevice::EUiCookie)
				{
				aStream.WriteUint32L(nextRemDev.UiCookie());
				}
			if (rdSetMask & CBTDevice::EDeviceName)
				{
				__ASSERT_DEBUG(nextRemDev.DeviceName().Length() <= KMaxTUint8, PANIC(KBTBackupPanicCat, EBTBURMgrDescriptorLengthTooLong));
				aStream.WriteUint8L(nextRemDev.DeviceName().Length());
				aStream.WriteL(nextRemDev.DeviceName(), nextRemDev.DeviceName().Length());
				}
			if (rdSetMask & CBTDevice::EFriendlyName)
				{
				__ASSERT_DEBUG(nextRemDev.FriendlyName().Length() <= KMaxTUint8, PANIC(KBTBackupPanicCat, EBTBURMgrDescriptorLengthTooLong));
				aStream.WriteUint8L(nextRemDev.FriendlyName().Length());
				aStream.WriteL(nextRemDev.FriendlyName(), nextRemDev.FriendlyName().Length());
				}
			}
		}
	else
		{
		User::Leave(KErrNotFound);
		}
	}

/**
Reads registry data from a given stream and stores within this instance.
Any registry data currently held is cleared first.
@param aStream The stream to ready the registry data from.
**/
void CBTRegistryBURData::ReadFromStreamL(RReadStream& aStream)
	{
	LOG_FUNC

	// This follows a specific file-format (tied to the registry version information), which is documented seperately.
	
	// Note that we dont make *ANY* assumptions as to what data should always be available, since the restore file could be corrupt.
	// We will fail the restore if it turns out we do not have enough information, or if we have invalid information.
	
	// WARNING: When a new registry version is produced, this implementaton of backup/restore support will need modification.
	// This assert should be updated to include all supported registry versions.
	__ASSERT_DEBUG(KRegistryDBVersionMajor == 1 && KRegistryDBVersionMinor == 2, PANIC(KBTBackupPanicCat, EBTBURMgrUnsupportedRegistryVersion));
	
	// Extract version information and continue if we can handle it.
	TUint32 regVersionMajor = aStream.ReadUint32L();
	TUint32 regVersionMinor = aStream.ReadUint32L();
	if (regVersionMajor == KRegistryDBVersionMajor && regVersionMinor == KRegistryDBVersionMinor)
		{
		// We support this version and this version only - read in registry data
		ClearRegistryData();

		iLocalDevice = new(ELeave) TBTLocalDevice;
		
		// Note version information for this registry
		iRegistryVersionMajor = regVersionMajor;
		iRegistryVersionMinor = regVersionMinor;

		// Persistence table:
		// Local device address and name only (both fields are mandatory).
		TBTDevAddr address;
		TPtr8 addrPtr(address.Des());
		aStream.ReadL(addrPtr, KBTDevAddrSize);
		if (addrPtr.Length() == KBTDevAddrSize)
			{
			iLocalDevice->SetAddress(address);
			}
		else
			{
			User::Leave(KErrCorrupt);
			}

		TInt deviceNameLength = static_cast<TInt>(aStream.ReadUint8L());
		if (deviceNameLength <= KMaxBluetoothNameLen)
			{
			RBuf8 deviceName;
			deviceName.CreateL(KMaxBluetoothNameLen);
			CleanupClosePushL(deviceName);

			aStream.ReadL(deviceName,deviceNameLength);
			if (deviceName.Length() == deviceNameLength)
				{
				iLocalDevice->SetDeviceName(deviceName);
				}
			else
				{
				User::Leave(KErrCorrupt);
				}
			CleanupStack::PopAndDestroy(&deviceName);
			}
		else
			{
			User::Leave(KErrCorrupt);
			}
		
		// Remote devices table:
		TInt rdCount = aStream.ReadUint32L();
		for (TInt i = 0; i < rdCount; i++)
			{
			// First read bitmask of available data
			TUint32 rdSetMask = aStream.ReadUint32L();

			// Then read available data from file and store
			CBTDevice *rdInstance = CBTDevice::NewLC();
			TBTNamelessDevice& rdNamelessDevInstance = rdInstance->AsNamelessDevice();
			TSecureId rdSid = aStream.ReadUint32L();

			// Remote BT address is mandatory
			if (rdSetMask & TBTNamelessDevice::EAddress)
				{
				TBTDevAddr rdAddr;
				TPtr8 rdAddrPtr(rdAddr.Des());
				aStream.ReadL(rdAddrPtr, KBTDevAddrSize);

				// Ensure address is of required length and is non-zero
				if (rdAddrPtr.Length() == KBTDevAddrSize && rdAddr != TBTDevAddr())
					{
					rdInstance->SetDeviceAddress(rdAddr);
					}
				else
					{
					User::Leave(KErrCorrupt);
					}
				}
			else
				{
				User::Leave(KErrCorrupt);
				}

			if (rdSetMask & TBTNamelessDevice::EDeviceClass)
				{
				TBTDeviceClass rdClass = TBTDeviceClass(aStream.ReadUint32L());
				rdInstance->SetDeviceClass(rdClass);
				}

			if (rdSetMask & TBTNamelessDevice::ELinkKey)
				{
				TBTLinkKey rdLinkKey;
				aStream.ReadL(rdLinkKey, KHCILinkKeySize);
				if (rdLinkKey.Length() == KHCILinkKeySize)
					{
					TUint8 rdLinkKeyType = static_cast<TBTLinkKeyType>(aStream.ReadUint8L());
					// Ensure value is in the valid range (ELinkKeyCombination is zero so (pointless) comparison with this gives warnings).
					if (rdLinkKeyType <= ELinkKeyDebug)
						{
						rdInstance->SetLinkKey(rdLinkKey, static_cast<TBTLinkKeyType>(rdLinkKeyType));
						}
					else
						{
						User::Leave(KErrCorrupt);
						}

					}
				else
					{
					User::Leave(KErrCorrupt);
					}
				}

			if (rdSetMask & TBTNamelessDevice::EGlobalSecurity)
				{
				TBTDeviceSecurity rdGlobSec;
				TUint8 globSecValue = aStream.ReadUint8L();
				// Ensure value is in the valid range (lower value is zero so (pointless) comparison with this gives warnings).
				if (globSecValue <= KBTBURMgrMaxGlobalSecurityValue)
					{
					rdGlobSec.SetSecurityValue(globSecValue);

					TUint32 passKeyMinLen = aStream.ReadUint32L();

					if (passKeyMinLen <= KHCIPINCodeSize)
						{
						rdGlobSec.SetPasskeyMinLength(passKeyMinLen);
						rdInstance->SetGlobalSecurity(rdGlobSec);
						}
					else
						{
						User::Leave(KErrCorrupt);
						}
					}
				else
					{
					User::Leave(KErrCorrupt);
					}
				}

			if (rdSetMask & TBTNamelessDevice::EPageScanRepMode)
				{
				TUint8 rdPageScanRepMode = aStream.ReadUint8L();
				// Ensure value is in the valid range (lower value is zero so (pointless) comparison with this gives warnings).
				if (rdPageScanRepMode <= EPageScanModeR2)
					{
					rdNamelessDevInstance.SetPageScanRepMode(rdPageScanRepMode);
					}
				else
					{
					User::Leave(KErrCorrupt);
					}
				}

			if (rdSetMask & TBTNamelessDevice::EPageScanMode)
				{
				TUint8 rdPageScanMode = aStream.ReadUint8L();
				// Ensure value is in the valid range (lower value is zero so (pointless) comparison with this gives warnings).
				if (rdPageScanMode <= KBTBURMgrMaxPageScanMode)
					{
					rdNamelessDevInstance.SetPageScanMode(rdPageScanMode);
					}
				else
					{
					User::Leave(KErrCorrupt);
					}
				}

			if (rdSetMask & TBTNamelessDevice::EPageScanPeriodMode)
				{
				TUint8 rdPageScanPeriodMode = aStream.ReadUint8L();
				// Ensure value is in the valid range (lower value is zero so (pointless) comparison with this gives warnings).
				if (rdPageScanPeriodMode <= KBTBURMgrMaxPageScanPeriodMode)
					{
					rdNamelessDevInstance.SetPageScanPeriodMode(rdPageScanPeriodMode);
					}
				else
					{
					User::Leave(KErrCorrupt);
					}
				}

			if (rdSetMask & TBTNamelessDevice::EClockOffset)
				{
				TUint16 rdClockOffset = aStream.ReadUint16L();
				rdNamelessDevInstance.SetClockOffset(rdClockOffset);
				}

			if (rdSetMask & TBTNamelessDevice::EUsed)
				{
				TInt32 usedU = aStream.ReadInt32L();
				TInt32 usedL = aStream.ReadInt32L();
				TInt64 rdUsed = MAKE_TINT64(usedU, usedL);
				rdNamelessDevInstance.SetUsed(rdUsed);
				}

			if (rdSetMask & TBTNamelessDevice::ESeen)
				{
				TInt32 seenU = aStream.ReadInt32L();
				TInt32 seenL = aStream.ReadInt32L();
				TInt64 rdSeen = MAKE_TINT64(seenU, seenL);
				rdNamelessDevInstance.SetSeen(rdSeen);	
				}

			if (rdSetMask & TBTNamelessDevice::EPassKey)
				{
				TBTPinCode rdPassKey;
				aStream.ReadL(rdPassKey, KHCIPINCodeSize + 1);
				if (rdPassKey.Length() == KHCIPINCodeSize + 1)
					{
					rdInstance->SetPassKey(rdPassKey);
					}
				else
					{
					User::Leave(KErrCorrupt);
					}
				}
			
			if (rdSetMask & TBTNamelessDevice::EUiCookie)
				{
				TUint32 rdUiCookie = aStream.ReadUint32L();
				rdNamelessDevInstance.SetUiCookie(rdUiCookie);
				}

			if (rdSetMask & CBTDevice::EDeviceName)
				{
				TInt rdNameLen = static_cast<TInt>(aStream.ReadUint8L());
				if (rdNameLen <= KMaxBluetoothNameLen)
					{
					RBuf8 rdName;
					rdName.CreateL(KMaxBluetoothNameLen);
					CleanupClosePushL(rdName);
					
					aStream.ReadL(rdName, rdNameLen);
					if (rdName.Length() == rdNameLen)
						{
						rdInstance->SetDeviceNameL(rdName);
						}
					else
						{
						User::Leave(KErrCorrupt);
						}
					CleanupStack::PopAndDestroy(&rdName);
					}
				else
					{
					User::Leave(KErrCorrupt);
					}
				}

			if (rdSetMask & CBTDevice::EFriendlyName)
				{
				TInt rdFriendlyNameLen = static_cast<TInt>(aStream.ReadUint8L());
				if (rdFriendlyNameLen <= KMaxFriendlyNameLen)
					{
					RBuf rdFriendlyName;
					rdFriendlyName.CreateL(KMaxFriendlyNameLen);
					CleanupClosePushL(rdFriendlyName);

					aStream.ReadL(rdFriendlyName, rdFriendlyNameLen);
					if (rdFriendlyName.Length() == rdFriendlyNameLen)
						{
						rdInstance->SetFriendlyNameL(rdFriendlyName);
						}
					else
						{
						User::Leave(KErrCorrupt);
						}
					CleanupStack::PopAndDestroy(&rdFriendlyName);
					}
				else
					{
					User::Leave(KErrCorrupt);
					}
				}
			
			// Data now stored, add rdInstance to our remote devices list
			iRemoteDevices->AppendL(rdInstance);
			iRemoteDevicesSid->AppendL(rdSid);
			CleanupStack::Pop(rdInstance);
			}

		// Mark that we now have registry data 
		iHasRegistryData = ETrue;
		}
	}

/**
Reads registry data from the registry into this instance.
Any existing registry data held in this instance is cleared first.
@param aRegistry The CBTRegistry instance to use for registry access.
**/
void CBTRegistryBURData::ReadFromRegistryL(CBTRegistry& aRegistry)
	{
	LOG_FUNC

	ClearRegistryData();

	// The meta information from our registry is known.
	iRegistryVersionMajor = KRegistryDBVersionMajor;
	iRegistryVersionMinor = KRegistryDBVersionMinor;

	// Persistence table
	iLocalDevice = aRegistry.GetLocalDeviceL();

	// Remote device table
	// Create a view on the table.
	RBTDbQuery query;
	CleanupClosePushL(query);
	TBTRegistrySearch searchCriteria;
	searchCriteria.FindAll();
	query.SearchL(searchCriteria);
	TDbBookmark bookmark;
	RDbView* view = aRegistry.OpenViewL(query, bookmark);
	CleanupCloseDeletePushL(view);

	// Populate iRemoteDevices from the view.
	while (!view->AtEnd())
		{
		CBTDevice *next = aRegistry.GetNextDeviceL(*view, bookmark, ETrue);
		CleanupStack::PushL(next);
		
		// CBTRegisty::CreatingProcessUidL() requires a rowset with exactly one row.
		// So we have to requery for this device to get a singular row.
		TDbBookmark singleBookmark;
		RDbView* singleView = aRegistry.OpenDeviceL(next->BDAddr(), singleBookmark);
		CleanupCloseDeletePushL(singleView);
		TSecureId nextSid = aRegistry.CreatingProcessUidL(*singleView);
		CleanupStack::PopAndDestroy(singleView);
		
		iRemoteDevices->AppendL(next);
		iRemoteDevicesSid->AppendL(nextSid);
		
		CleanupStack::Pop(next);	// iRemoteDevices now takes ownership
		}

	CleanupStack::PopAndDestroy(2, &query);	// view and query

	// Mark that we now have registry data 
	iHasRegistryData = ETrue;
	}

/**
Updates the persistence table of the registry with local device name held in this instance.
The update is only performed if the local name held in this instance differs from that currently held in the registry.
@param aRegistry The CBTRegistry instance to use for registry access.
@return ETrue if an update was made to the registry.
**/
TBool CBTRegistryBURData::WriteLocalDeviceNameToRegistryL(CBTRegistry& aRegistry) const
	{
	LOG_FUNC

	TBool updateDone = EFalse;
	
	TBTLocalDevice* localDevice = aRegistry.GetLocalDeviceL();
	CleanupStack::PushL(localDevice);

	const TDesC8& localName = GetLocalDeviceNameL();

	if (localDevice->DeviceName() != localName)
		{
		localDevice->SetDeviceName(localName);
		aRegistry.UpdateLocalDeviceL(*localDevice);
		updateDone = ETrue;
		}

	CleanupStack::PopAndDestroy(localDevice);

	return updateDone;
	}

/**
Updates an entry of the remote devices table of the registry with data held in this instance.
If the remote device already exists in the registry, the registry version is updated only if it
does not hold a link key.
Otherwise, the remote device is added to the registry.
@param aRegistry The CBTRegistry instance to use for registry access.
@param aDeviceIndex The remote device held in this CBTRegistryBURData instance to be written to the registry.
@return ETrue if an update was made to the registry.
**/
TBool CBTRegistryBURData::WriteRemoteDeviceToRegistryL(CBTRegistry& aRegistry, TInt aDeviceIndex) const
	{
	LOG_FUNC

	TBool updateDone = EFalse;
	
	// Get device and SID
	const CBTDevice& nextRemDevice = GetRemoteDeviceL(aDeviceIndex);
	TSecureId nextRemDeviceSid = GetRemoteDeviceEntrySidL(aDeviceIndex);

	// Try to add device to registry. If this fails with KErrAlreadyExists, then update existing device.
	TRAPD(err, aRegistry.CreateDeviceL(nextRemDevice, nextRemDevice.IsValidUiCookie(), nextRemDeviceSid));

	if (err == KErrNone)
		{
		// New device added successfully
		updateDone = ETrue;
		}
	else if (err == KErrAlreadyExists)
		{
		// Device already exists. Extract and examine
		TDbBookmark bookmark;
		RDbView* view = aRegistry.OpenDeviceL(nextRemDevice.BDAddr(), bookmark);
		CleanupCloseDeletePushL(view);
		CBTDevice *regDev = aRegistry.GetNextDeviceL(*view, bookmark, ETrue);
		CleanupStack::PushL(regDev);

		if (!regDev->IsValidLinkKey())
			{
			// No link key - safe to restore remote device from file.
			view->FirstL();
			aRegistry.UpdateDeviceL(*view, nextRemDevice);
			updateDone = ETrue;
			}

		CleanupStack::PopAndDestroy(2, view);
		}
	else
		{
		// Unexpected error - pass upward for handling
		User::Leave(err);
		}

	return updateDone;
	}

void CBTRegistryBURData::GetRegistryVersionL(TUint32& aRegistryVersionMajor, TUint32& aRegistryVersionMinor) const
	{
	LOG_FUNC

	if (!HasRegistryData())
		{
		User::Leave(KErrNotFound);
		}
	
	aRegistryVersionMajor = iRegistryVersionMajor;
	aRegistryVersionMinor = iRegistryVersionMinor;
	}

const TDesC8& CBTRegistryBURData::GetLocalDeviceNameL() const
	{
	LOG_FUNC

	if (!HasRegistryData())
		{
		User::Leave(KErrNotFound);
		}

	__ASSERT_DEBUG(iLocalDevice, PANIC(KBTBackupPanicCat, EBTBURMgrExpectedDataMissing));
	return iLocalDevice->DeviceName();
	}

TBool CBTRegistryBURData::IsLocalAddressNonZeroL() const
	{
	LOG_FUNC

	TBTDevAddr zeroAddr;
	return !IsLocalAddressEqualL(zeroAddr);
	}

TBool CBTRegistryBURData::IsLocalAddressEqualL(TBTDevAddr& aAddr) const
	{
	LOG_FUNC

	if (!HasRegistryData())
		{
		User::Leave(KErrNotFound);
		}

	__ASSERT_DEBUG(iLocalDevice, PANIC(KBTBackupPanicCat, EBTBURMgrExpectedDataMissing));
	return (iLocalDevice->Address() == aAddr);
	}

TInt CBTRegistryBURData::CountRemoteDevicesL() const
	{
	LOG_FUNC

	if (!HasRegistryData())
		{
		User::Leave(KErrNotFound);
		}
	
	__ASSERT_DEBUG(iRemoteDevices->Count() == iRemoteDevicesSid->Count(), PANIC(KBTBackupPanicCat, EBTBURMgrArraySizeMisMatch));	
	return iRemoteDevices->Count();
	}

const CBTDevice& CBTRegistryBURData::GetRemoteDeviceL(TInt aDeviceIndex) const
	{
	LOG_FUNC

	if (!HasRegistryData() || !((aDeviceIndex >= 0) && (aDeviceIndex < iRemoteDevices->Count())))
		{
		User::Leave(KErrNotFound);
		}

	__ASSERT_DEBUG(iRemoteDevices->Count() == iRemoteDevicesSid->Count(), PANIC(KBTBackupPanicCat, EBTBURMgrArraySizeMisMatch));	
	return *(iRemoteDevices->At(aDeviceIndex));
	}

TSecureId CBTRegistryBURData::GetRemoteDeviceEntrySidL(TInt aDeviceIndex) const
	{
	LOG_FUNC

	if (!HasRegistryData() || !((aDeviceIndex >= 0) && (aDeviceIndex < iRemoteDevicesSid->Count())))
		{
		User::Leave(KErrNotFound);
		}
	
	__ASSERT_DEBUG(iRemoteDevices->Count() == iRemoteDevicesSid->Count(), PANIC(KBTBackupPanicCat, EBTBURMgrArraySizeMisMatch));	
	return iRemoteDevicesSid->At(aDeviceIndex);
	}

/**
Clears any registry data stored internally within this instance.
If no data is stored, this method does nothing.
**/
void CBTRegistryBURData::ClearRegistryData()
	{
	LOG_FUNC

	// This is a no-op if we have no registry data to clear
	if (HasRegistryData())
		{
		// Clear data held previously
		iRemoteDevicesSid->Reset();
		iRemoteDevices->ResetAndDestroy();
		delete iLocalDevice;
		iLocalDevice = NULL;

		iHasRegistryData = EFalse;
		}
	}

/**
CBTLocalAddressFetcher - Fetches the local device address, or waits for the addresss to become available, and 
passes to CBTManServerBURMgr.
**/
CBTLocalAddressFetcher* CBTLocalAddressFetcher::NewL(CBTManServerBURMgr& aBURMgr, CBTRegistry& aRegistry, TInt aPriority)
	{
	LOG_STATIC_FUNC

	CBTLocalAddressFetcher* result = new(ELeave) CBTLocalAddressFetcher(aBURMgr, aRegistry, aPriority);
	CleanupStack::PushL(result);
	result->ConstructL();
	CleanupStack::Pop(result);
	return result;
	}

CBTLocalAddressFetcher::CBTLocalAddressFetcher(CBTManServerBURMgr& aBURMgr, CBTRegistry& aRegistry, TInt aPriority) 
	: CActive(aPriority), 
	iBURMgr(aBURMgr), 
	iRegistry(aRegistry)
	{
	LOG_FUNC

	CActiveScheduler::Add(this);
	}

CBTLocalAddressFetcher::~CBTLocalAddressFetcher()
	{
	LOG_FUNC

	// Cancel any outstanding requests
	Cancel();
	iProperty.Close();
	}

void CBTLocalAddressFetcher::ConstructL()
	{
	LOG_FUNC

	// Attach to KPropertyKeyBluetoothGetLocalDeviceAddress
	LEAVEIFERRORL(iProperty.Attach(KPropertyUidBluetoothCategory, KPropertyKeyBluetoothGetLocalDeviceAddress));
	}

void CBTLocalAddressFetcher::FetchLocalAddress()
	{
	LOG_FUNC

	// Subscribe for local address, in case we need to wait
	SubscribeToGetLocalDeviceAddressKey();	

	// Attempt to read the BT address from KPropertyKeyBluetoothGetLocalDeviceAddress key
	// If key is not found or yields a zero address then try the registry.
	// If the registry also holds a zero address then wait for notification from the P&S key.
	TBuf8<KBTDevAddrSize> btAddrDes;
	TBTDevAddr btAddr;
	TInt err = iProperty.Get(btAddrDes);
	
	if (err != KErrNone)
		{
		// Key does not exist or could not be read.
		// Stack may not be loaded, so now try registry.
		// Ignore any errors; if we can't read this now then we will wait for the stack.
		TRAP_IGNORE(btAddr = GetLocalAddressFromRegistryL());
		}
	else
		{
		// Convert btAddrDes to TBTDevAddr if it has the required size.
		if (btAddrDes.Length() == KBTDevAddrSize)
			{
			btAddr = btAddrDes;	
			}

		if (btAddr == TBTDevAddr())
			{
			// Key exists, so stack is loaded, but address is zero, so now try registry
			// Ignore any errors; if we can't read this now then we will wait for the stack.
			TRAP_IGNORE(btAddr = GetLocalAddressFromRegistryL());
			}
		}
	
	// We have tried our best to get the local address.
	// If this is non-zero, provide to CBTManServerBURMgr instance. 
	// Otherwise, wait on P&S key
	if (btAddr != TBTDevAddr())
		{
		// We have a non-zero address.
		// Cancel subscription and inform CBTManServerBURMgr.
		Cancel();
		iBURMgr.SetLocalAddress(btAddr);
		}	
	}

void CBTLocalAddressFetcher::SubscribeToGetLocalDeviceAddressKey()
	{
	LOG_FUNC

	// Subscribe to KPropertyKeyBluetoothGetLocalDeviceAddress key
	iProperty.Subscribe(iStatus);
	SetActive();
	}

TBTDevAddr CBTLocalAddressFetcher::GetLocalAddressFromRegistryL()
	{
	LOG_FUNC
	
	TBTLocalDevice* regLocalDevice=iRegistry.GetLocalDeviceL();
	TBTDevAddr result=regLocalDevice->Address();
	delete regLocalDevice;
	
	return result;	
	}

void CBTLocalAddressFetcher::RunL()
	{
	LOG_FUNC

	if (iStatus == KErrNone)
		{
		// Obtain address and send to CBTManServerBURMgr
		TBuf8<KBTDevAddrSize> btAddrDes;
		TBTDevAddr btAddr;
		TInt err = iProperty.Get(btAddrDes);
				
		__ASSERT_DEBUG(err == KErrNone, PANIC(KBTBackupPanicCat, EBTBURMgrUnexpectedRPropertyError));
		
		if (err == KErrNone) 
			{
			btAddr = btAddrDes;
			iBURMgr.SetLocalAddress(btAddr);
			}
		}
	}

void CBTLocalAddressFetcher::DoCancel()
	{
	LOG_FUNC

	// Cancel our subscription
	iProperty.Cancel();
	}

/**
CBTActiveBackupDataClient - Active callback implementation (for restore notification)
**/
CBTActiveBackupDataClient* CBTActiveBackupDataClient::NewL(CBTManServerBURMgr& aBURMgr)
	{
	LOG_STATIC_FUNC

	CBTActiveBackupDataClient* result = new (ELeave) CBTActiveBackupDataClient(aBURMgr);
	return result;
	}

CBTActiveBackupDataClient::CBTActiveBackupDataClient(CBTManServerBURMgr& aBURMgr) 
	: iBURMgr(aBURMgr)
	{
	LOG_FUNC
	}

CBTActiveBackupDataClient::~CBTActiveBackupDataClient()
	{
	LOG_FUNC
	}

// Backup methods (not used)
void CBTActiveBackupDataClient::AllSnapshotsSuppliedL()
	{
	LOG_FUNC

	//Not supported
	User::Leave(KErrNotSupported);
	}
 
void CBTActiveBackupDataClient::ReceiveSnapshotDataL(TDriveNumber /*aDrive*/, TDesC8& /*aBuffer*/, TBool /*aLastSection*/)
	{
	LOG_FUNC

 	//Not supported
	User::Leave(KErrNotSupported);	
	}
 
TUint CBTActiveBackupDataClient::GetExpectedDataSize(TDriveNumber /*aDrive*/)
	{
	LOG_FUNC

	//Not supported - so expected size can be 0
	return 0;
	}
 
void CBTActiveBackupDataClient::GetSnapshotDataL(TDriveNumber /*aDrive*/, TPtr8& /*aBuffer*/, TBool& /*aFinished*/)
	{
	LOG_FUNC

	//Not supported
	User::Leave(KErrNotSupported);
	}
 
void CBTActiveBackupDataClient::InitialiseGetBackupDataL(TDriveNumber /*aDrive*/)
	{
	LOG_FUNC

	//Not supported
	User::Leave(KErrNotSupported);
	}
 
void CBTActiveBackupDataClient::GetBackupDataSectionL(TPtr8& /*aBuffer*/, TBool& /*aFinished*/)
	{
	LOG_FUNC

	//Not supported
	User::Leave(KErrNotSupported);
	}
         
//Restore methods (only the notification method RestoreComplete() is used)
 
void CBTActiveBackupDataClient::InitialiseRestoreBaseDataL(TDriveNumber /*aDrive*/)
	{
	LOG_FUNC

	//Note that we are doing a base restore.
	User::Leave(KErrNotSupported);
	}
 
void CBTActiveBackupDataClient::RestoreBaseDataSectionL(TDesC8& /*aBuffer*/, TBool /*aFinished*/)
	{
	LOG_FUNC

	//Not supported
	User::Leave(KErrNotSupported);
	}	
 
void CBTActiveBackupDataClient::InitialiseRestoreIncrementDataL(TDriveNumber /*aDrive*/)
	{
	LOG_FUNC

	//Not supported
	User::Leave(KErrNotSupported);
	}
 
void CBTActiveBackupDataClient::RestoreIncrementDataSectionL(TDesC8& /*aBuffer*/, TBool /*aFinished*/)
	{
	LOG_FUNC

	//Not supported
	User::Leave(KErrNotSupported);
	}
 
void CBTActiveBackupDataClient::RestoreComplete(TDriveNumber aDrive)
	{
	LOG_FUNC

	// Allow CBTManServerBURMgr instance to handle arrival of restore file.
	if (aDrive == RFs::GetSystemDrive())
		{
		iBURMgr.RestoreFileReady();
		}
	}
 
         
//General methods
 
void CBTActiveBackupDataClient::TerminateMultiStageOperation()
	{
	LOG_FUNC

	//Dont care - we will see the operation is cancelled when normal or unset mode is invoked.
	}
 
//Test methods
 
TUint CBTActiveBackupDataClient::GetDataChecksum(TDriveNumber /*aDrive*/)
	{
	LOG_FUNC

	//Return an invariant checksum
	return 0;
	}