/*
* Copyright (c) 2006-2008 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: Server-side implementation of BTEng
*
*/
#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
#include <bthci.h>
#include <bt_subscribe_partner.h>
#endif
#include <e32base.h>
#include <btmanclient.h>
#include <es_sock.h>
#include <btnotif.h>
#include <utf.h>
#include <ecom/ecom.h>
#include <centralrepository.h>
#include <featmgr.h>
#include <AknSmallIndicator.h>
#include <avkon.hrh>
#include <bt_subscribe.h>
#include "btengserver.h"
#include "btengsrvstate.h"
#include "btengsrvsession.h"
#include "btengsrvpluginmgr.h"
#include "btengsrvbbconnectionmgr.h"
#include "btengsrvkeywatcher.h"
#include "btengsdpdbhandler.h"
#include "btengclientserver.h"
#include "btengsecpolicy.h"
#include "btengprivatecrkeys.h"
#include "btengprivatepskeys.h"
#include "btengplugin.h"
#include "btengpairman.h"
#include "debug.h"
/** Bluetooth Engine server thread name */
_LIT( KBTEngThreadName, "BTEngine" );
/** Constant for converting minutes to microseconds */
const TInt64 KMinutesInMicroSecs = MAKE_TINT64( 0, 60000000 );
/** Idle timeout for shutting down the server (when power is off,
* and no clients are connected). The value is 3 seconds.
*/
const TInt KBTEngSrvIdleTimeout = 3000000;
/** Timeout for disabling Simple Pairing debug mode. The value is 30 minutes. */
const TInt KBTEngSspDebugModeTimeout = 1800000000;
/** Timeout for determining that BT is not turned off automatically.
* The value is 10.5 seconds.
*/
const TInt KBTEngBtAutoOffTimeout = 10500000;
/** Enumeration of bitmask for keeping track of different timers. */
enum TTimerQueued
{
ENone = 0x00,
EScanModeTimer = 0x01,
EIdleTimer = 0x02,
EAutoPowerOffTimer = 0x04,
ESspDebugModeTimer = 0x08
};
/** PubSub key read and write policies */
_LIT_SECURITY_POLICY_C2( KBTEngPSKeyReadPolicy,
ECapabilityLocalServices, ECapabilityReadDeviceData );
_LIT_SECURITY_POLICY_C2( KBTEngPSKeyWritePolicy,
ECapabilityLocalServices, ECapabilityWriteDeviceData );
// Default values for Major and Minor Device Class
const TUint16 KCoDDefaultServiceClass = EMajorServiceTelephony | EMajorServiceObjectTransfer | EMajorServiceNetworking;
const TUint8 KCoDDefaultMajorDeviceClass = EMajorDevicePhone;
const TUint8 KCoDDefaultMinorDeviceClass = EMinorDevicePhoneSmartPhone;
// ======== LOCAL FUNCTIONS ========
// ---------------------------------------------------------------------------
// Constructs and returns an application object.
// ---------------------------------------------------------------------------
//
void RunServerL()
{
TRACE_FUNC_ENTRY
User::RenameThread( KBTEngThreadName );
// Create and install the active scheduler for this thread.
CActiveScheduler* scheduler = new( ELeave ) CActiveScheduler();
CleanupStack::PushL( scheduler );
CActiveScheduler::Install( scheduler );
// create the server (leave it on the cleanup stack)
CBTEngServer* btServer = CBTEngServer::NewLC();
// Initialisation complete, now signal the client
RProcess::Rendezvous( KErrNone );
// The server is not up and running.
TRACE_INFO( ( _L( "[BTENG]\t BTEng server now up and running" ) ) )
// The active scheduler runs during the lifetime of this thread.
CActiveScheduler::Start();
// Cleanup the server and scheduler.
CleanupStack::PopAndDestroy( btServer );
CleanupStack::PopAndDestroy( scheduler );
TRACE_FUNC_EXIT
}
// ---------------------------------------------------------------------------
// Completes the message and panics the client.
// ---------------------------------------------------------------------------
//
void PanicClient( const RMessage2& aMessage, TInt aPanic )
{
TRACE_INFO( ( _L( "[BTENG]\t PanicClient: Reason %d" ), aPanic ) )
aMessage.Panic( KBTEngPanic, aPanic );
}
// ---------------------------------------------------------------------------
// Panic the server.
// ---------------------------------------------------------------------------
//
void PanicServer( TInt aPanic )
{
TRACE_INFO( ( _L( "[BTENG]\t PanicClient: Reason %d" ), aPanic ) )
User::Panic( KBTEngPanic, aPanic );
}
// ======== MEMBER FUNCTIONS ========
// ---------------------------------------------------------------------------
// C++ default constructor
// (Priority is not as high as in prev. architecture, but should be enough)
// ---------------------------------------------------------------------------
//
CBTEngServer::CBTEngServer()
: CPolicyServer( EPriorityHigh, KBTEngServerPolicy )
{
iEnableDutMode = EFalse;
}
// ---------------------------------------------------------------------------
// Symbian 2nd-phase constructor
// ---------------------------------------------------------------------------
//
void CBTEngServer::ConstructL()
{
TRACE_FUNC_ENTRY
// No need to check _BT flag here, as the server can
// only be started by BTEng components.
// Add the server to the active scheduler (from CServer2):
StartL( KBTEngServerName );
User::LeaveIfError( RProperty::Define( KPSUidBluetoothTestingMode, KBTDutEnabled,
RProperty::EInt, KBTEngPSKeyReadPolicy,
KBTEngPSKeyWritePolicy ) );
User::LeaveIfError( RProperty::Define( KPSUidBluetoothTestingMode, KBTSspDebugmode,
RProperty::EInt, KBTEngPSKeyReadPolicy,
KBTEngPSKeyWritePolicy ) );
User::LeaveIfError( RProperty::Define( KPSUidBluetoothEnginePrivateCategory,
KBTBlockDevAddr, RProperty::EText,
KBTEngPSKeyReadPolicy,
KBTEngPSKeyWritePolicy ) );
User::LeaveIfError( RProperty::Define( KPSUidBluetoothEnginePrivateCategory,
KBTOutgoingPairing,
RProperty::EByteArray,
KBTEngPSKeyReadPolicy,
KBTEngPSKeyWritePolicy) );
User::LeaveIfError( RProperty::Define( KPSUidBluetoothEnginePrivateCategory,
KBTConnectionTimeStamp,
RProperty::EByteArray,
KBTEngPSKeyReadPolicy,
KBTEngPSKeyWritePolicy) );
User::LeaveIfError( RProperty::Define( KPSUidBluetoothEnginePrivateCategory,
KBTTurnBTOffQueryOn,
RProperty::EInt,
KBTEngPSKeyReadPolicy,
KBTEngPSKeyWritePolicy) );
User::LeaveIfError( RProperty::Define( KPSUidBluetoothEnginePrivateCategory,
KBTNotifierLocks,
RProperty::EByteArray,
KBTEngPSKeyReadPolicy,
KBTEngPSKeyWritePolicy) );
User::LeaveIfError( iSocketServ.Connect() );
LoadBTPowerManagerL();
// The server is only started by its client, so leave the state
// machine in Init state. A request to turn power on will follow
// usually immediately.
iServerState = CBTEngSrvState::NewL( this );
iWatcher = CBTEngSrvKeyWatcher::NewL( this );
iPluginMgr = CBTEngSrvPluginMgr::NewL( this );
iBBConnMgr = CBTEngSrvBBConnMgr::NewL( iSocketServ );
User::LeaveIfError( iBTRegServ.Connect() );
iPairMan = CBTEngPairMan::NewL( *this );
TCallBack idleCb( IdleTimerCallBack, this );
iIdleCallBack.Set( idleCb );
TCallBack sspCb( DebugModeTimerCallBack, this );
iDebugModeCallBack.Set( sspCb );
TCallBack scanModeCb( ScanModeTimerCallBack, this );
iScanModeCallBack.Set( scanModeCb );
TCallBack powerOffCb( AutoPowerOffCallBack, this );
iPowerOffCallBack.Set( powerOffCb );
iTimer = CDeltaTimer::NewL(CActive::EPriorityLow);
iEnterpriseEnablementMode = BluetoothFeatures::EnterpriseEnablementL();
TRACE_INFO( ( _L( "iEnterpriseEnablementMode = %d" ), iEnterpriseEnablementMode) )
if ( iEnterpriseEnablementMode == BluetoothFeatures::EDisabled )
{
SetVisibilityModeL( EBTVisibilityModeNoScans, 0 );
}
TRACE_FUNC_EXIT
}
// ---------------------------------------------------------------------------
// NewL
// ---------------------------------------------------------------------------
//
CBTEngServer* CBTEngServer::NewLC()
{
CBTEngServer* self = new( ELeave ) CBTEngServer();
CleanupStack::PushL( self );
self->ConstructL();
return self;
}
// ---------------------------------------------------------------------------
// Destructor
// ---------------------------------------------------------------------------
//
CBTEngServer::~CBTEngServer()
{
TRACE_FUNC_ENTRY
if( iTimer )
{
iTimerQueued = ENone;
iTimer->Remove( iScanModeCallBack );
iTimer->Remove( iPowerOffCallBack );
iTimer->Remove( iIdleCallBack);
iTimer->Remove( iDebugModeCallBack );
}
RProperty::Delete( KPSUidBluetoothTestingMode, KBTDutEnabled );
RProperty::Delete( KPSUidBluetoothTestingMode, KBTSspDebugmode );
RProperty::Delete( KPSUidBluetoothEnginePrivateCategory, KBTBlockDevAddr );
RProperty::Delete( KPSUidBluetoothEnginePrivateCategory, KBTOutgoingPairing );
RProperty::Delete( KPSUidBluetoothEnginePrivateCategory, KBTConnectionTimeStamp );
RProperty::Delete( KPSUidBluetoothEnginePrivateCategory, KBTTurnBTOffQueryOn );
RProperty::Delete( KPSUidBluetoothEnginePrivateCategory, KBTNotifierLocks );
delete iTimer;
delete iSdpDbHandler;
delete iWatcher;
delete iPluginMgr;
delete iBBConnMgr;
delete iServerState;
delete iPairMan;
iPowerMgr.Close();
iSocketServ.Close();
iBTRegServ.Close();
}
// ---------------------------------------------------------------------------
// Turn BT on or off.
// ---------------------------------------------------------------------------
//
void CBTEngServer::SetPowerStateL( TBTPowerStateValue aState, TBool aTemporary )
{
TRACE_FUNC_ARG( ( _L( "setting power state %d" ), (TInt) aState ) )
if ( aState == EBTPowerOn && iEnterpriseEnablementMode == BluetoothFeatures::EDisabled )
{
TRACE_INFO( ( _L( "\tno we're not... Bluetooth is enterprise-IT-disabled" ) ) )
User::Leave(KErrNotSupported);
}
TBTPowerStateValue currentState = EBTPowerOff;
CheckTemporaryPowerStateL( currentState, aState, aTemporary );
if( ( currentState == aState || ( aTemporary && aState == EBTPowerOff ) ) && iServerState->CurrentOperation() == CBTEngSrvState::ESrvOpIdle )
{
// The requested power state is already active, ignore silently.
// We don't return an error here, as there is no error situation.
TRACE_INFO( ( _L( "SetPowerStateL: nothing to do" ) ) )
if(currentState == aState)
{
// Make sure that the CenRep key is in sync.
// During boot-up, the pwoer is set from the CenRep key, so we could
// end up out-of-sync.
TRACE_INFO( ( _L( "SetPowerStateL: currentState == aState" ) ) )
UpdateCenRepPowerKeyL( aState );
}
return;
}
if( aState )
{
// Hardware power on is the first step.
User::LeaveIfError( SetPowerState( aState ) );
}
else
{
//Prevent BT visibility in the situation when we turn OFF BT Engine
//but FM Radio is still alive
SetVisibilityModeL( EBTVisibilityModeNoScans, 0 );
// Hardware power off is the last step.
// First disconnect all plug-ins.
iPluginMgr->DisconnectAllPlugins();
}
// We only signal that BT is on after everything has completed (through
// the CenRep power state key), so that all services are initialized.
// We signal that BT is off immediately though, so that our clients will
// not try to use BT during power down.
iServerState->StartStateMachineL( (TBool) aState );
TRACE_FUNC_EXIT
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
void CBTEngServer::SetVisibilityModeL( TBTVisibilityMode aMode, TInt aTime )
{
TRACE_FUNC_ENTRY
if ( aMode != EBTVisibilityModeNoScans && iEnterpriseEnablementMode == BluetoothFeatures::EDisabled )
{
TRACE_INFO( ( _L( "\tnot changing anything... Bluetooth is enterprise-IT-disabled" ) ) )
User::Leave(KErrNotSupported);
}
TInt err = KErrNone;
iTimerQueued &= ~EScanModeTimer;
iTimer->Remove( iScanModeCallBack );
if( aMode != EBTVisibilityModeNoScans )
{
CRepository* cenRep = CRepository::NewL( KCRUidBTEngPrivateSettings );
err = cenRep->Set( KBTDiscoverable, aMode );
delete cenRep;
}
if( !err && aMode == EBTVisibilityModeTemporary )
{
// We need TInt64 here, as the max. time in microseconds for the
// max. value (1 hour) is larger than KMaxTInt32.
TInt64 timeMicroSec = MAKE_TINT64( 0, aTime );
timeMicroSec = timeMicroSec * KMinutesInMicroSecs;
TTimeIntervalMicroSeconds interval( timeMicroSec );
// Queue callback to set the visibility back to hidden.
err = iTimer->QueueLong( interval, iScanModeCallBack );
iTimerQueued |= EScanModeTimer;
aMode = EBTVisibilityModeGeneral;
}
else if( !err && iRestoreVisibility )
{
// The user overrides, do not restore visibility mode anymore.
iRestoreVisibility = EFalse;
}
if( !err )
{
err = RProperty::Set( KUidSystemCategory,
KPropertyKeyBluetoothSetScanningStatus, aMode );
}
TBool hiddenMode = ( aMode == EBTVisibilityModeHidden );
if( !err && aMode != EBTVisibilityModeNoScans )
{
// In hidden mode, we only accept connections from paired devices.
err = RProperty::Set( KUidSystemCategory,
KPropertyKeyBluetoothSetAcceptPairedOnlyMode,
hiddenMode );
}
User::LeaveIfError( err ); // To communicate the result to the client.
TRACE_FUNC_EXIT
}
// ---------------------------------------------------------------------------
// The method is called when BT stack scanning mode P&S key is changed
// ---------------------------------------------------------------------------
//
void CBTEngServer::UpdateVisibilityModeL( TInt aStackScanMode )
{
TRACE_FUNC_ENTRY
TRACE_INFO( ( _L( "[BTEng]\t aStackScanMode: %d" ), aStackScanMode ) )
TBTVisibilityMode currentMode;
CRepository* cenRep = CRepository::NewLC( KCRUidBTEngPrivateSettings );
User::LeaveIfError( cenRep->Get( KBTDiscoverable, (TInt&) currentMode ) );
// In case we are in temp visibility mode, we cannot always know whether the BT stack
// scan mode key was set by some external party or by us in SetVisibilityModeL above.
// Therefore we cannot stop the timer in case aMode is EBTVisibilityModeGeneral and
// currentmode is EBTVisibilityModeTemporary
if( !( currentMode == EBTVisibilityModeTemporary && aStackScanMode == EBTVisibilityModeGeneral ) )
{
// Cancel the timer and queue it again if needed.
iTimerQueued &= ~EScanModeTimer;
iTimer->Remove( iScanModeCallBack );
if( currentMode != aStackScanMode )
{
if( aStackScanMode == EPageScanOnly || aStackScanMode == EInquiryAndPageScan )
{
User::LeaveIfError( cenRep->Set( KBTDiscoverable, aStackScanMode ) );
}
else if( aStackScanMode == EInquiryScanOnly )
{
// We don't support ENoScansEnabled nor EInquiryScanOnly mode
// -> Consider these as same as Hidden
User::LeaveIfError( cenRep->Set( KBTDiscoverable, EBTVisibilityModeHidden ) );
}
else if( aStackScanMode == ENoScansEnabled )
{
//We don't change KBTDiscoverable here, because ENoScansEnabled
//indicates BT/SYSTEM shutdown is happening
}
}
}
SetUiIndicatorsL();
CleanupStack::PopAndDestroy( cenRep );
TRACE_FUNC_EXIT
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
void CBTEngServer::DisconnectAllL()
{
TRACE_FUNC_ENTRY
iPluginMgr->DisconnectAllPlugins();
TCallBack cb( DisconnectAllCallBack, this );
iBBConnMgr->DisconnectAllLinksL( cb );
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
void CBTEngServer::SetDutMode( TInt aDutMode )
{
TRACE_FUNC_ARG( ( _L( "DUT mode %d" ), aDutMode ) )
if (aDutMode == EBTDutOff)
{
return;
}
TInt powerState = EBTPowerOff;
CRepository* cenrep = NULL;
TRAPD(err, cenrep = CRepository::NewL(KCRUidBluetoothPowerState));
if (!err && cenrep)
{
cenrep->Get(KBTPowerState, powerState);
delete cenrep;
cenrep = NULL;
}
else
{
return;
}
if (powerState == EBTPowerOn)
{
#ifndef __WINS__
TInt err = iDutMode.Open();
TRACE_FUNC_ARG( ( _L( "Open DUT mode handle err %d" ), err) )
if(!err)
{
iDutMode.ActivateDutMode();
iDutMode.Close();
}
#endif //__WINS__
}
iEnableDutMode = EFalse;
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
void CBTEngServer::ScanModeTimerCompletedL()
{
TRACE_FUNC_ENTRY
// The timer has completed, so remove our reference as well.
iTimerQueued &= ~EScanModeTimer;
iTimer->Remove( iScanModeCallBack );
SetVisibilityModeL( EBTVisibilityModeHidden, 0 );
TBTPowerStateValue power = EBTPowerOff;
TInt err = GetHwPowerState( (TBTPowerStateValue&) power );
if( !err && power )
{
// Show a notification to the user
TBTGenericInfoNotiferParamsPckg pckg;
pckg().iMessageType = EBTVisibilityTimeout;
RNotifier notifier;
TInt err = notifier.Connect();
if( !err )
{
err = notifier.StartNotifier( KBTGenericInfoNotifierUid, pckg );
notifier.Close();
}
}
TRACE_FUNC_RES( ( _L( "result: %d" ), err ) )
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
void CBTEngServer::DisconnectAllCompleted()
{
// Check if we are powering off. Otherwise we have just been
// requested to disconnect all, e.g. for system shutdown.
if( iServerState->CurrentOperation() == CBTEngSrvState::EPowerOff )
{
(void) SetPowerState( EFalse );
// Put the state machine into idle state.
iServerState->ChangeState();
}
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
void CBTEngServer::DispatchPluginMessageL( const RMessage2& aMessage )
{
iPluginMgr->ProcessCommandL( aMessage );
}
// ---------------------------------------------------------------------------
// A new session has been added, update the session count.
// ---------------------------------------------------------------------------
//
void CBTEngServer::AddSession()
{
iSessionCount++;
// Remove the idle timer.
iTimerQueued &= ~EIdleTimer;
iTimer->Remove( iIdleCallBack );
}
// ---------------------------------------------------------------------------
// A session has been ended, update the session count.
// ---------------------------------------------------------------------------
//
void CBTEngServer::RemoveSession( TBool aAutoOff )
{
TRACE_FUNC_ENTRY
TRACE_INFO( ( _L( "[CBTEngServer]\t aAutoOff %d"), aAutoOff ))
TRACE_INFO( ( _L( "[CBTEngServer]\t iSessionCount %d"), iSessionCount ))
iSessionCount--;
if( aAutoOff )
{
TRAP_IGNORE( SetPowerStateL( EBTPowerOff, ETrue ) );
}
else
{
CheckIdle();
}
TRACE_FUNC_EXIT
}
// ---------------------------------------------------------------------------
// From class CPolicyServer.
// Checks version compatibility and constructs a new session object.
// ---------------------------------------------------------------------------
//
CSession2* CBTEngServer::NewSessionL( const TVersion& aVersion,
const RMessage2& aMessage ) const
{
TRACE_FUNC_ENTRY
(void) aMessage;
// Compare our version with client-side version, CServer2 requires that
// we leave if they are not compatible.
// Compatible is interpreted here as equal or greater than our version.
TVersion srvVersion( KBTEngServerVersionMajor, KBTEngServerVersionMinor,
KBTEngServerVersionBuild );
if( !User::QueryVersionSupported( aVersion, srvVersion ) )
{
// EFalse is returned if our version is not less than or
// equal to the client version.
User::Leave( KErrNotSupported );
}
return CBTEngSrvSession::NewL();
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
void CBTEngServer::CheckIdle()
{
TRACE_FUNC_ENTRY
__ASSERT_DEBUG( iTimer, PanicServer( EBTEngPanicMemberVarIsNull ) );
iTimerQueued &= ~EIdleTimer;
iTimer->Remove( iIdleCallBack );
if( iSessionCount <= 0 )
{
// No more sessions, check the power state.
TBTPowerStateValue pwr = EBTPowerOff;
TInt err = GetHwPowerState( pwr );
TRACE_INFO( ( _L( "[BTEng]\t No sessions; power state: %d" ), pwr ) )
if( !err &&!pwr
&& iServerState->CurrentOperation() == CBTEngSrvState::ESrvOpIdle )
{
TRACE_INFO( ( _L( "[BTEng]\t Power off; starting shutdown timer" ) ) )
// power is off, start the shutdown timer.
TTimeIntervalMicroSeconds32 interval = KBTEngSrvIdleTimeout;
iTimer->Queue( interval, iIdleCallBack );
iTimerQueued |= EIdleTimer;
}
}
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
TInt CBTEngServer::SetPowerState( TBool aState )
{
TRACE_FUNC_ENTRY
TInt err = KErrNone;
// HCIv2 power state type is inverted from BTPM-defined type...
TBTPowerState powerState = (TBTPowerState) !aState;
TRequestStatus status;
#ifndef __WINS__
iPowerMgr.SetPower( powerState, NULL, status );
User::WaitForRequest( status );
err = status.Int();
#else //__WINS__
iPowerState = powerState;
err = KErrNone;
#endif //__WINS__
if( !err && aState )
{
TInt dutMode;
err = RProperty::Get( KPSUidBluetoothTestingMode, KBTDutEnabled, dutMode );
if( !err && dutMode == EBTDutOn )
{
// Set the DUT mode key to OFF since DUT mode is disabled at this point
err = RProperty::Set( KPSUidBluetoothTestingMode, KBTDutEnabled, EBTDutOff );
}
// Set the local name straight away, so that an error
// in the loading of the BT stack will be detected here.
// Note that the HCIv2 power manager already loads the BT stack,
// so we do not need to do that here.
TRAP(err,SetLocalNameL());
}
if( err )
{
// Power off if an error occurred during power on sequence.
#ifndef __WINS__
// This cannot happen in emulator environment.
iPowerMgr.SetPower( EBTOff, NULL, status );
User::WaitForRequest( status );
#endif //__WINS__
}
TRACE_FUNC_RES( ( _L( "result: %d" ), err ) )
return err;
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
TInt CBTEngServer::SetLocalNameL()
{
TRACE_FUNC_ENTRY
RHostResolver hostResolver;
TBuf<KMaxBluetoothNameLen> name;
name.Zero();
TBuf<KMaxBluetoothNameLen> tmpName;
TInt err = RProperty::Get(KPropertyUidBluetoothCategory,
KPropertyKeyBluetoothGetDeviceName, name);
if(err == KErrNone)
{
err = RProperty::Get(KPropertyUidBluetoothCategory,
KPropertyKeyBluetoothSetDeviceName, tmpName);
if (tmpName.Compare(name))
{
// The name has not yet been updated. Use the new one.
name.Copy(tmpName);
}
}
// if name hasn't been set, check whats in the registry
if (err || !name.Length())
{
GetLocalNameFromRegistryL(name);
}
TRACE_INFO( ( _L( "[CBTEngServer]\t localDev.DeviceName(): '%S'" ), &name))
CleanupClosePushL(hostResolver);
User::LeaveIfError(hostResolver.Open(iSocketServ, KBTAddrFamily, KBTLinkManager));
User::LeaveIfError(hostResolver.SetHostName(name));
CleanupStack::PopAndDestroy(&hostResolver);
return KErrNone;
}
// ---------------------------------------------------------------------------
// Checks whats written in the registry in order to set host name
// ---------------------------------------------------------------------------
//
TInt CBTEngServer::GetLocalNameFromRegistryL(TDes& aName)
{
TRACE_FUNC_ENTRY
RBTRegServ btRegServ;
RBTLocalDevice btReg;
TBTLocalDevice localDev;
CleanupClosePushL(btRegServ);
CleanupClosePushL(btReg);
// In case of error, read local name from registry
aName.Zero();
User::LeaveIfError(btRegServ.Connect());
User::LeaveIfError(btReg.Open(btRegServ));
// Read the BT local name from BT Registry.
User::LeaveIfError(btReg.Get(localDev));
CleanupStack::PopAndDestroy(2,&btRegServ);
// BT registry keeps the device name in UTF-8 format, convert to unicode.
// The error can be > 0 if there are unconverted characters.
TInt err = CnvUtfConverter::ConvertToUnicodeFromUtf8(aName, localDev.DeviceName());
if (err != KErrNone)
User::Leave(err);
return KErrNone;
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
void CBTEngServer::SetClassOfDeviceL()
{
TRACE_FUNC_ENTRY
TUint16 serviceClass = KCoDDefaultServiceClass;
// Check from feature manager if stereo audio is enabled.
FeatureManager::InitializeLibL();
TBool supported = FeatureManager::FeatureSupported( KFeatureIdBtStereoAudio );
FeatureManager::UnInitializeLib();
if( supported )
{
// A2DP spec says we should set this bit as we are a SRC
serviceClass |= EMajorServiceCapturing;
}
// These values may nayway be overridden by HCI
TBTDeviceClass cod( serviceClass, KCoDDefaultMajorDeviceClass,
KCoDDefaultMinorDeviceClass );
// Ignore error, it is non-critical
(void) RProperty::Set( KPropertyUidBluetoothControlCategory,
KPropertyKeyBluetoothSetDeviceClass, cod.DeviceClass() );
TRACE_FUNC_EXIT
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
void CBTEngServer::InitBTStackL()
{
TRACE_FUNC_ENTRY
iBBConnMgr->Subscribe();
TBTVisibilityMode visibility = EBTVisibilityModeHidden;
CRepository* cenRep = CRepository::NewL( KCRUidBTEngPrivateSettings );
TInt err = cenRep->Get( KBTDiscoverable, (TInt&) visibility );
delete cenRep;
if (iRestoreVisibility == EFalse)
{
if( err || visibility == EBTVisibilityModeTemporary && !( iTimerQueued & EScanModeTimer ) )
{
visibility = EBTVisibilityModeHidden;
}
SetVisibilityModeL( visibility, 0 );
}
SetClassOfDeviceL();
TBool sspDebugMode = EFalse;
(void) RProperty::Get( KPropertyUidBluetoothCategory,
KPropertyKeyBluetoothGetSimplePairingDebugMode,
(TInt&) sspDebugMode );
// Only set debug mode to off if it is on, to prevent a loop notifications.
if( sspDebugMode )
{
sspDebugMode = EFalse;
// Add LeaveIfError if unsuccessful
(void) RProperty::Set(KPropertyUidBluetoothCategory,
KPropertyKeyBluetoothSetSimplePairingDebugMode,
(TInt) sspDebugMode );
}
TRACE_FUNC_EXIT
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
void CBTEngServer::StopBTStackL()
{
TRACE_FUNC_ENTRY
TBTVisibilityMode visibility = EBTVisibilityModeHidden;
CRepository* cenRep = CRepository::NewL( KCRUidBTEngPrivateSettings );
// Ignore error here; if we can't read it, likely we can't set it either.
(void) cenRep->Get( KBTDiscoverable, (TInt&) visibility );
delete cenRep;
if( visibility == EBTVisibilityModeTemporary )
{
visibility = EBTVisibilityModeHidden;
SetVisibilityModeL( visibility, 0 ); // Also cancels scan mode timer.
}
// Stop listening to events
iBBConnMgr->Unsubscribe();
// Disconnect all links
TCallBack cb( DisconnectAllCallBack, this );
iBBConnMgr->DisconnectAllLinksL( cb );
// Results in a callback (which is called directly when there are no links).
TRACE_FUNC_EXIT
}
// ---------------------------------------------------------------------------
// Update the power state CenRep key.
// ---------------------------------------------------------------------------
//
void CBTEngServer::UpdateCenRepPowerKeyL( TBTPowerStateValue aState )
{
TRACE_FUNC_ENTRY
CRepository* cenrep = CRepository::NewLC( KCRUidBluetoothPowerState );
User::LeaveIfError( cenrep->Set( KBTPowerState, (TInt) aState ) );
CleanupStack::PopAndDestroy( cenrep );
TRACE_FUNC_EXIT
}
// ---------------------------------------------------------------------------
// Loads the BT Power Manager; leaves if it cannot be loaded.
// ---------------------------------------------------------------------------
//
void CBTEngServer::LoadBTPowerManagerL()
{
TRACE_FUNC_ENTRY
TRACE_INFO( ( _L( "[CBTEngServer]\t Using HCI API v2 power manager" ) ) )
User::LeaveIfError( iPowerMgr.Open() );
#ifndef __WINS__
TRequestStatus status( KRequestPending );
iPowerMgr.SetPower( EBTOff, NULL, status );
User::WaitForRequest( status );
status = ( status.Int() == KErrAlreadyExists ? KErrNone : status.Int() );
User::LeaveIfError( status.Int() );
#else //__WINS__
iPowerState = EBTOff;
#endif //__WINS__
TRACE_FUNC_EXIT
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
void CBTEngServer::ManageDIServiceL( TBool aState )
{
TRACE_FUNC_ENTRY
// The session with the SDP database is kept open during the
// lifetime of BTEng server. This ensures that the database server
// will not destroy itself (and the SDP record) when there are no
// sessions anymore (which it would do). This also ensures that other
// sessions (of BT Engine Discovery API) can be closed without the SDP
// database being destroyed, and so saving memory.
TInt err = KErrNone;
if( !iSdpDbHandler )
{
iSdpDbHandler = CBTEngSdpDbHandler::NewL();
}
if( aState && !iDiSdpRecHandle )
{
// Use TRAP here, because the function will leave if
// the required CenRep keys are not available.
TInt vendorId = 0;
TInt productId = 0;
TRAP( err, GetProductIdsL( vendorId, productId ) );
#ifdef __WINS__
err = KErrNone; // Ignore error and load with S60 RnD default values.
#endif //__WINS__
if( !err )
{
TUUID uuid( EBTProfileDI );
iSdpDbHandler->RegisterSdpRecordL( uuid, vendorId, productId,
iDiSdpRecHandle );
}
}
if( ( !aState || err ) && iDiSdpRecHandle )
{
// Either we are shutting down, or we did not manage
// to fill the record with required data -> delete it.
iSdpDbHandler->DeleteSdpRecordL( iDiSdpRecHandle );
iDiSdpRecHandle = 0;
delete iSdpDbHandler;
iSdpDbHandler = NULL;
}
User::LeaveIfError( err ); // To pass back the result of the operation.
TRACE_FUNC_EXIT
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
void CBTEngServer::GetProductIdsL( TInt& aVendorId, TInt& aProductId )
{
TRACE_FUNC_ENTRY
CRepository* cenRep = CRepository::NewLC( KCRUidBluetoothEngine );
User::LeaveIfError( cenRep->Get( KBTVendorID, aVendorId ) );
User::LeaveIfError( cenRep->Get( KBTProductID, aProductId ) );
CleanupStack::PopAndDestroy( cenRep ); //cenRep
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
void CBTEngServer::SetUiIndicatorsL()
{
TRACE_FUNC_ENTRY
TInt powerState = EBTPowerOff;
TBTVisibilityMode visibilityMode = EBTVisibilityModeHidden;
CRepository* cenrep = NULL;
TInt phys = 0;
TInt connecting = 0;
cenrep = CRepository::NewLC( KCRUidBluetoothPowerState );
User::LeaveIfError( cenrep->Get( KBTPowerState, powerState ) );
CleanupStack::PopAndDestroy( cenrep );
if( powerState == EBTPowerOff )
{
SetIndicatorStateL( EAknIndicatorBluetoothModuleOn, EAknIndicatorStateOff );
SetIndicatorStateL( EAknIndicatorBluetooth, EAknIndicatorStateOff );
SetIndicatorStateL( EAknIndicatorBluetoothModuleOnVisible, EAknIndicatorStateOff );
SetIndicatorStateL( EAknIndicatorBluetoothVisible, EAknIndicatorStateOff );
}
else if( powerState == EBTPowerOn )
{
RProperty::Get( KPropertyUidBluetoothCategory, KPropertyKeyBluetoothPHYCount, phys );
RProperty::Get( KPropertyUidBluetoothCategory, KPropertyKeyBluetoothConnecting, connecting );
cenrep = CRepository::NewLC( KCRUidBTEngPrivateSettings );
User::LeaveIfError( cenrep->Get( KBTDiscoverable, (TInt&) visibilityMode ) );
CleanupStack::PopAndDestroy( cenrep );
if( visibilityMode == EBTVisibilityModeHidden )
{
if ( connecting ) // BT connecting and hidden
{
SetIndicatorStateL( EAknIndicatorBluetoothModuleOn, EAknIndicatorStateOff );
SetIndicatorStateL( EAknIndicatorBluetooth, EAknIndicatorStateAnimate );
}
else if ( phys > 0 ) // BT connection active and hidden
{
SetIndicatorStateL( EAknIndicatorBluetoothModuleOn, EAknIndicatorStateOff );
SetIndicatorStateL( EAknIndicatorBluetooth, EAknIndicatorStateOn );
}
else // BT connection not active and hidden
{
SetIndicatorStateL( EAknIndicatorBluetoothModuleOn, EAknIndicatorStateOn );
SetIndicatorStateL( EAknIndicatorBluetooth, EAknIndicatorStateOff );
}
SetIndicatorStateL( EAknIndicatorBluetoothModuleOnVisible, EAknIndicatorStateOff );
SetIndicatorStateL( EAknIndicatorBluetoothVisible, EAknIndicatorStateOff );
}
else if( visibilityMode == EBTVisibilityModeGeneral || visibilityMode == EBTVisibilityModeTemporary )
{
if ( connecting ) // BT connecting and visible
{
SetIndicatorStateL( EAknIndicatorBluetoothModuleOnVisible, EAknIndicatorStateOff );
SetIndicatorStateL( EAknIndicatorBluetoothVisible, EAknIndicatorStateAnimate );
}
else if ( phys > 0 ) // BT connection active and visible
{
SetIndicatorStateL( EAknIndicatorBluetoothModuleOnVisible, EAknIndicatorStateOff );
SetIndicatorStateL( EAknIndicatorBluetoothVisible, EAknIndicatorStateOn );
}
else // BT connection not active and visible
{
SetIndicatorStateL( EAknIndicatorBluetoothModuleOnVisible, EAknIndicatorStateOn );
SetIndicatorStateL( EAknIndicatorBluetoothVisible, EAknIndicatorStateOff );
}
SetIndicatorStateL( EAknIndicatorBluetoothModuleOn, EAknIndicatorStateOff );
SetIndicatorStateL( EAknIndicatorBluetooth, EAknIndicatorStateOff );
}
}
TRACE_FUNC_EXIT
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
void CBTEngServer::SetIndicatorStateL( const TInt aIndicator, const TInt aState )
{
CAknSmallIndicator* indicator = CAknSmallIndicator::NewLC( TUid::Uid( aIndicator ) );
indicator->SetIndicatorStateL( aState );
CleanupStack::PopAndDestroy( indicator ); //indicator
}
// ---------------------------------------------------------------------------
// Gets the current HW power state.
// For now this is a separate method to isolate the different variations.
// ---------------------------------------------------------------------------
//
TInt CBTEngServer::GetHwPowerState( TBTPowerStateValue& aState )
{
TRACE_FUNC_ENTRY
TInt err = KErrNone;
#ifndef __WINS__
err = iPowerMgr.GetPower( (TBTPowerState&) aState, NULL );
#else //__WINS__
aState = (TBTPowerStateValue) iPowerState;
#endif //__WINS__
// HCIv2 power state type is inverted from BTPM-defined type...
aState = (TBTPowerStateValue) !aState;
TRACE_FUNC_ARG( ( _L( "Power state is %d, result %d" ), (TInt) aState, err ) )
return err;
}
// ---------------------------------------------------------------------------
// Check the power state and if BT gets turned off automatically.
// ---------------------------------------------------------------------------
//
void CBTEngServer::CheckTemporaryPowerStateL( TBTPowerStateValue& aCurrentState,
TBTPowerStateValue aNewState, TBool aTemporary )
{
TRACE_FUNC_ENTRY
User::LeaveIfError( GetHwPowerState( aCurrentState ) );
if( !aTemporary )
{
// Force the new power state, so clear all auto switch off flags.
// If power is off, this will anyway be ignored.
iAutoOffClients = 0;
iAutoSwitchOff = EFalse;
TCallBack cb;
iBBConnMgr->SetAutoSwitchOff( EFalse, cb );
if( iRestoreVisibility && aCurrentState == EBTPowerOn )
{
// Set visibility mode back to the value selected by the user.
SetVisibilityModeL( EBTVisibilityModeGeneral, 0 );
iRestoreVisibility = EFalse;
}
}
else
{
if( aCurrentState == aNewState )
{
if( iAutoSwitchOff && aNewState == EBTPowerOn )
{
iAutoOffClients++;
if( iTimerQueued & EAutoPowerOffTimer )
{
iTimer->Remove( iPowerOffCallBack );
iTimerQueued &= ~EAutoPowerOffTimer;
}
}
}
else if( iAutoSwitchOff || aNewState == EBTPowerOn )
{
aNewState == EBTPowerOff ? iAutoOffClients-- : iAutoOffClients++;
iAutoSwitchOff = ETrue;
if( aNewState == EBTPowerOff && iAutoOffClients <= 0 )
{
TCallBack powerOffCb( AutoPowerOffCallBack, this );
iBBConnMgr->SetAutoSwitchOff( ETrue, powerOffCb );
iTimer->Queue( KBTEngBtAutoOffTimeout, iPowerOffCallBack );
iTimerQueued |= EAutoPowerOffTimer;
}
else if( aNewState == EBTPowerOn )
{
CRepository* cenRep = CRepository::NewLC( KCRUidBTEngPrivateSettings );
TBTVisibilityMode visibility = EBTVisibilityModeGeneral;
TInt err = cenRep->Get( KBTDiscoverable, (TInt&) visibility );
CleanupStack::PopAndDestroy( cenRep );
if( !err && visibility == EBTVisibilityModeGeneral )
{
SetVisibilityModeL( EBTVisibilityModeHidden, 0 );
iRestoreVisibility = ETrue;
}
if( iTimerQueued & EAutoPowerOffTimer )
{
iTimer->Remove( iPowerOffCallBack );
iTimerQueued &= ~EAutoPowerOffTimer;
}
}
}
}
if( iAutoOffClients < 0 )
{
iAutoOffClients = 0;
}
TRACE_FUNC_EXIT
}
// ---------------------------------------------------------------------------
// Check the power state and if BT gets turned off automatically.
// This method is invoked either when the timer has expired, or
// if there are no more connections while the timer was running.
// ---------------------------------------------------------------------------
//
void CBTEngServer::CheckAutoPowerOffL()
{
TRACE_FUNC_ENTRY
if ( iAutoOffClients > 0 )
{
TRACE_INFO( ( _L( "[CBTEngServer]\t iAutoOffClients %d"), iAutoOffClients ))
return;
}
TInt linkCount = 0;
TInt err = RProperty::Get( KPropertyUidBluetoothCategory,
KPropertyKeyBluetoothGetPHYCount, linkCount );
if( !err && !linkCount )
{
TRACE_INFO( ( _L( "[CBTEngServer]\t SetPowerStateL( EBTPowerOff, EFalse );")))
SetPowerStateL( EBTPowerOff, EFalse );
}
else
{
if( iRestoreVisibility )
{
// Set visibility mode back to the value selected by the user.
SetVisibilityModeL( EBTVisibilityModeGeneral, 0 );
iRestoreVisibility = EFalse;
}
// show note if non-audio connection exists
if ( !iPluginMgr->CheckAudioConnectionsL() )
{
RNotifier notifier;
TInt err = notifier.Connect();
if( !err )
{
TRequestStatus status;
TBTGenericInfoNotiferParamsPckg pckg;
pckg().iMessageType = EBTStayPowerOn;
TBuf8<sizeof(TInt)> result;
//notifier.StartNotifier( KBTGenericInfoNotifierUid, pckg, result );
notifier.StartNotifierAndGetResponse( status,
KBTGenericInfoNotifierUid,
pckg, result ); // Reply buffer not used.
User::WaitForRequest( status );
notifier.Close();
}
}
iAutoOffClients = 0;
iAutoSwitchOff = EFalse;
TCallBack cb;
iBBConnMgr->SetAutoSwitchOff( EFalse, cb );
}
TRACE_FUNC_EXIT
}
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
void CBTEngServer::CheckSspDebugModeL( TBool aDebugMode )
{
TRACE_FUNC_ARG( ( _L( "SSP debug mode state %d" ), (TInt) aDebugMode ) )
TBTPowerState pwr = EBTOff;
TBool currentMode = EFalse;
TInt err = RProperty::Get( KPropertyUidBluetoothCategory,
KPropertyKeyBluetoothGetSimplePairingDebugMode,
(TInt&) currentMode );
#ifndef __WINS__
err = iPowerMgr.GetPower( pwr, NULL ); // Treat error as power off.
#else //__WINS__
pwr = iPowerState;
#endif //__WINS__
if( err || pwr == EBTOff )
{
iTimerQueued &= ~ESspDebugModeTimer;
iTimer->Remove( iDebugModeCallBack );
// Only set debug mode to off if it is on, to prevent a loop notifications.
if( currentMode )
{
(void) RProperty::Set( KPropertyUidBluetoothCategory,
KPropertyKeyBluetoothSetSimplePairingDebugMode,
(TInt) aDebugMode );
}
// In case of an error in getting the power state, turn BT off.
// If BT is already off, this call will be ignored.
SetPowerStateL( EBTPowerOff, EFalse );
}
else if( aDebugMode )
{
// Ignore if there already is a timer queued.
if( !( iTimerQueued & ESspDebugModeTimer ) )
{
iTimer->Queue( TTimeIntervalMicroSeconds32( KBTEngSspDebugModeTimeout ),
iDebugModeCallBack );
iTimerQueued &= ESspDebugModeTimer;
err = RProperty::Set(KPropertyUidBluetoothCategory,
KPropertyKeyBluetoothSetSimplePairingDebugMode,
(TInt) aDebugMode );
}
}
else
{
// Power is on, and debug mode is set to off.
TInt linkCount = 0;
err = RProperty::Get( KPropertyUidBluetoothCategory,
KPropertyKeyBluetoothGetPHYCount, linkCount );
if( err || !linkCount )
{
(void) RProperty::Set(KPropertyUidBluetoothCategory,
KPropertyKeyBluetoothSetSimplePairingDebugMode,
(TInt) aDebugMode );
SetPowerStateL( EBTPowerOff, EFalse );
}
else
{
// There are still existing connections, queue the
// timer again for half the period.
iTimer->Queue( TTimeIntervalMicroSeconds32( KBTEngSspDebugModeTimeout / 2 ),
iDebugModeCallBack );
iTimerQueued &= ESspDebugModeTimer;
}
}
TRACE_FUNC_EXIT
}
// ---------------------------------------------------------------------------
// returns the reference of pairing manager
// ---------------------------------------------------------------------------
//
CBTEngPairMan& CBTEngServer::PairManager()
{
return *iPairMan;
}
// ---------------------------------------------------------------------------
// Delegate the information to pairing manager
// ---------------------------------------------------------------------------
//
void CBTEngServer::RemoteRegistryChangeDetected()
{
iPairMan->RemoteRegistryChangeDetected();
}
// ---------------------------------------------------------------------------
// Access the reference of RSockServ
// ---------------------------------------------------------------------------
//
RSocketServ& CBTEngServer::SocketServ()
{
return iSocketServ;
}
// ---------------------------------------------------------------------------
// Access the reference of RBTRegSrv
// ---------------------------------------------------------------------------
//
RBTRegServ& CBTEngServer::BTRegServ()
{
return iBTRegServ;
}
// ---------------------------------------------------------------------------
// Ask plugin manager the connection status of the specified device
// ---------------------------------------------------------------------------
//
TBTEngConnectionStatus CBTEngServer::IsDeviceConnected( const TBTDevAddr& aAddr )
{
TBTEngConnectionStatus status = EBTEngNotConnected;
if ( iPluginMgr )
{
status = iPluginMgr->IsDeviceConnected( aAddr );
}
return status;
}
// ---------------------------------------------------------------------------
// Static callback for temporary visibility mode.
// ---------------------------------------------------------------------------
//
TInt CBTEngServer::ScanModeTimerCallBack( TAny* aPtr )
{
__ASSERT_ALWAYS(aPtr, PanicServer(EBTEngPanicArgumentIsNull) );
TRAPD( err, ( (CBTEngServer*) aPtr )->ScanModeTimerCompletedL() );
return err;
}
// ---------------------------------------------------------------------------
// Static callback for disconnecting all Baseband connections.
// ---------------------------------------------------------------------------
//
TInt CBTEngServer::DisconnectAllCallBack( TAny* aPtr )
{
__ASSERT_ALWAYS(aPtr, PanicServer(EBTEngPanicArgumentIsNull) );
( (CBTEngServer*) aPtr )->DisconnectAllCompleted();
return KErrNone;
}
// ---------------------------------------------------------------------------
// Static callback for idle timer timeout. We are shutting down ourselves.
// ---------------------------------------------------------------------------
//
TInt CBTEngServer::IdleTimerCallBack( TAny* aPtr )
{
(void) aPtr;
CActiveScheduler::Stop(); // Will destroy CBTEngServer
return KErrNone;
}
// ---------------------------------------------------------------------------
// Static callback for idle timer timeout. Turn off BT to get it out of
// debug mode. If there are existing connections, queue the timer again.
// ---------------------------------------------------------------------------
//
TInt CBTEngServer::DebugModeTimerCallBack( TAny* aPtr )
{
// Set our internal debug mode key to off. Ignore error, not critical here.
(void) RProperty::Set( KPSUidBluetoothTestingMode, KBTSspDebugmode, EFalse );
__ASSERT_ALWAYS(aPtr, PanicServer(EBTEngPanicArgumentIsNull) );
TRAP_IGNORE( ( (CBTEngServer*) aPtr )->CheckSspDebugModeL( EFalse ) );
return KErrNone;
}
// ---------------------------------------------------------------------------
// Static callback for auto power off.
// ---------------------------------------------------------------------------
//
TInt CBTEngServer::AutoPowerOffCallBack( TAny* aPtr )
{
TRACE_FUNC_ENTRY
__ASSERT_ALWAYS(aPtr, PanicServer(EBTEngPanicArgumentIsNull) );
CBTEngServer* server = (CBTEngServer*) aPtr;
server->iTimerQueued &= ~EAutoPowerOffTimer;
TRAPD( err, server->CheckAutoPowerOffL() );
return err;
}
BluetoothFeatures::TEnterpriseEnablementMode CBTEngServer::EnterpriseEnablementMode() const
{
return iEnterpriseEnablementMode;
}
// ======== GLOBAL FUNCTIONS ========
// ---------------------------------------------------------------------------
// Main function of the executable.
// ---------------------------------------------------------------------------
//
GLDEF_C TInt E32Main()
{
__UHEAP_MARK;
TRACE_FUNC_ENTRY
CTrapCleanup* cleanup = CTrapCleanup::New();
TInt err = KErrNoMemory;
if ( cleanup )
{
TRAP( err, RunServerL() );
delete cleanup;
}
__UHEAP_MARKEND;
return err;
}