diff -r 8a27654f7b62 -r 20fda83a6398 bluetoothmgmt/btmgr/BTManServer/btmanserverburmgr.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bluetoothmgmt/btmgr/BTManServer/btmanserverburmgr.cpp Mon Mar 15 12:44:59 2010 +0200 @@ -0,0 +1,2020 @@ +// 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 +#include +#include +#include +#include +#include "btmanserverutil.h" +#include "btmanserverburmgr.h" +#include "BTManServer.h" + +#include "btmanclient.h" +#include "btdevice.h" +#include +#include +#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 and +the local device name is updated in the registry. The restore of the remote devices table 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(); + // Attempt to update local device name in the registry (best efforts only) + TRAP_IGNORE(UpdateLocalDeviceNameL()); + } + +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->RestoreRemoteDeviceTableL(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(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(fsSession.Rename(KBTManServerBackupFileName, KBTManServerRestoreFileName)); + fsSession.Close(); + } + } + +/** +Parses the restore file and updates the loal device name in the registry. +This update takes place as soon as the restore file is available, regardless of whether or not the local device +address matches that held in the registry. If the local device name has already been set to a non-default value, +then it is not modified. +**/ +void CBTManServerBURMgr::UpdateLocalDeviceNameL() + { + LOG_FUNC + + CBTRestoreHandler* restoreHandler = CBTRestoreHandler::NewL(*this, iBTManServer); + CleanupStack::PushL(restoreHandler); + + restoreHandler->RestoreLocalDeviceNameL(); + + CleanupStack::PopAndDestroy(restoreHandler); + } + +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::RestoreLocalDeviceNameL() + { + LOG_FUNC + + LoadRestoreDataL(); + + // If the local device name is still default, restore without validating the local address + // (otherwise we will not be able to restore this field before the next stack start, which may cause problems with some UIs) + CBTRegistry& registry = iManServer.Registry(); + if (iRegistryData->WriteLocalDeviceNameToRegistryL(registry)) + { + NotifyLocalTableChange(); + } + } + +void CBTRestoreHandler::RestoreRemoteDeviceTableL(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 of remote devices table + CBTRegistry& registry = iManServer.Registry(); + + 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 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(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(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(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(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(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(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 +if the registry currently holds a default name. +@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; + + // Update device name only if the registry has a default name + TBTLocalDevice defaultDevice; + TRAP_IGNORE(aRegistry.GetDefaultDeviceFromIniL(defaultDevice)); + + if (!defaultDevice.IsValidDeviceName()) + { + // Could not obtain a default name - use KDefaultLocalName instead + defaultDevice.SetDeviceName(KDefaultLocalName); + } + + TBTLocalDevice* localDevice = aRegistry.GetLocalDeviceL(); + CleanupStack::PushL(localDevice); + + if (localDevice->DeviceName() == defaultDevice.DeviceName()) + { + // Local device name is default, update with restored value. + localDevice->SetDeviceName(GetLocalDeviceNameL()); + 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 KErrAlreadExists, 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 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 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; + }