bluetoothengine/bteng/src/btengsrvpluginmgr.cpp
author hgs
Wed, 29 Sep 2010 13:09:05 +0300
changeset 65 001a94c71129
parent 28 7e2761e776bd
permissions -rw-r--r--
201037

/*
* Copyright (c) 2006 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:  Helper class for BTEng server to manage and interface with
*                the profile plug-ins the profile plug-ins.
*
*/



#include <ecom/ecom.h>
#include <btsdp.h>
#include <centralrepository.h>
#include <featmgr.h>

#include "btengsrvpluginmgr.h"
#include "btengserver.h"
#include "btengsrvsession.h"
#include "btengsrvbbconnectionmgr.h"
#include "btengprivatecrkeys.h"
#include "debug.h"
#include <btfeaturescfg.h>

/**  UID for BTEng plug-ins */
const TUid KBTEngPluginUid = { 0x2000277B };
/**  ECOM registration info for BTSAP plugin */
_LIT8( KEComBTSapPlugin, "112D" );
/**  The message argument which holds the Bluetooth address. */
const TInt KBTEngAddrSlot = 0;
/**  The message argument which holds the connection status parameter. */
const TInt KBTEngParamSlot = 1;

// ======== MEMBER FUNCTIONS ========

// ---------------------------------------------------------------------------
// C++ default constructor
// ---------------------------------------------------------------------------
//
CBTEngSrvPluginMgr::CBTEngSrvPluginMgr( CBTEngServer* aServer )
:    iServer( aServer )
    {
    }


// ---------------------------------------------------------------------------
// Symbian 2nd-phase constructor
// ---------------------------------------------------------------------------
//
void CBTEngSrvPluginMgr::ConstructL()
    {
    }


// ---------------------------------------------------------------------------
// NewL
// ---------------------------------------------------------------------------
//
CBTEngSrvPluginMgr* CBTEngSrvPluginMgr::NewL( CBTEngServer* aServer )
    {
    CBTEngSrvPluginMgr* self = new( ELeave ) CBTEngSrvPluginMgr( aServer );
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
    return self;
    }


// ---------------------------------------------------------------------------
// Destructor
// ---------------------------------------------------------------------------
//
CBTEngSrvPluginMgr::~CBTEngSrvPluginMgr()
    {
    TRACE_FUNC_ENTRY
    iPluginInfoArray.Close();
    iPluginArray.ResetAndDestroy();
    iUuidContainter.Close();
    REComSession::FinalClose();
    TRACE_FUNC_EXIT
    }


// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
void CBTEngSrvPluginMgr::ProcessCommandL( const RMessage2& aMessage )
    {
    TRACE_FUNC_ENTRY
    TInt opcode = aMessage.Function();
    TInt err = KErrNone;
    TBTDevAddrPckgBuf addr;
    switch( opcode )
        {
        case EBTEngConnectDevice:
            {
            aMessage.ReadL( KBTEngAddrSlot, addr );
            TBTEngDevClassPkg cod;
            aMessage.ReadL( KBTEngParamSlot, cod );
            err = Connect( addr(), TBTDeviceClass( cod() ) );
            }
            break;
        case EBTEngCancelConnectDevice:
            {
            aMessage.ReadL( KBTEngAddrSlot, addr );
            err = CancelConnect( addr() );
            }
            break;
        case EBTEngDisconnectDevice:
            {
            aMessage.ReadL( KBTEngAddrSlot, addr );
            TPckgBuf<TUint> discType;
            aMessage.ReadL( KBTEngParamSlot, discType );
            err = Disconnect( addr(), (TBTDisconnectType) discType() );
            }
            break;
        case EBTEngIsDeviceConnected:
            {
            aMessage.ReadL( KBTEngAddrSlot, addr );
            TBTEngConnectionStatus connectStatus = EBTEngNotConnected;
            connectStatus = IsDeviceConnected( addr() );
            TBTEngParamPkg statusPkg( connectStatus );
            aMessage.WriteL( KBTEngParamSlot, statusPkg );
            }
            break;
        case EBTEngIsDeviceConnectable:
            {
            aMessage.ReadL( KBTEngAddrSlot, addr );
            TBTEngDevClassPkg cod;
            aMessage.ReadL( KBTEngParamSlot, cod );
            TBool connectable = ( GetConnectablePluginIndex(cod(), addr() ) != KErrNotFound ) ;
            TPckgBuf<TBool> connPkg( connectable );
            aMessage.WriteL( 2, connPkg );
            }
            break;
        case EBTEngGetConnectedAddresses:
            {
            TPckgBuf<TBTProfile> profile;
            aMessage.ReadL( KBTEngParamSlot, profile );
            RBTDevAddrArray addrArray;
            if( profile() == EBTProfileUndefined )
                {
                    // Get all baseband connections
                iServer->iBBConnMgr->GetConnectedAddressesL( addrArray );
                }
            else
                {
                err = GetConnectedAddresses( addrArray, profile() );
                }
            HBufC8* buf = HBufC8::NewLC( aMessage.GetDesMaxLengthL( KBTEngAddrSlot ) );
            TPtr8 ptr = buf->Des();
            aMessage.ReadL( KBTEngAddrSlot, ptr );
            ptr.Zero();
            for( TInt i = 0; i < addrArray.Count(); i++ )
                {
                addr = addrArray[ i ];
                ptr.Append( addr );
                }
            aMessage.WriteL( KBTEngAddrSlot, ptr );
            CleanupStack::PopAndDestroy( buf );
            }
            break;
        default:
            {
            TRACE_INFO( ( _L( "ProcessCommandL: bad request (%d)" ), 
                           aMessage.Function() ) )
            err = KErrArgument;
            }
            break;
        }
    User::LeaveIfError( err );
    TRACE_FUNC_EXIT
    }


// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
void CBTEngSrvPluginMgr::DisconnectAllPlugins()
    {
    TRACE_FUNC_ENTRY
    TBTDevAddr nullAddr;
    for( TInt i = 0; i < iPluginArray.Count(); i++ )
        {
        (void) iPluginArray[ i ]->Disconnect( nullAddr, EBTDiscImmediate );
        }
    }


// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
void CBTEngSrvPluginMgr::LoadProfilePluginsL( const TEComResolverParams aParams )
    {
    TRACE_FUNC_ENTRY
    if( aParams.DataType().Length() )
        {
            // This is a request to enable a specific service, e.g. BT SAP.
        REComSession::ListImplementationsL( KBTEngPluginUid, aParams, 
                                             iPluginInfoArray );
        }
    else
        {
        if( iPluginArray.Count() > 0 || iPluginInfoArray.Count() > 0 )
            {
                // Could be the case if we received a command to turn BT on 
                // halfway through a power down sequence. Just ignore.
            return;
            }
        iPluginInfoArray.Reset();
        iPluginArray.Reset();
        REComSession::ListImplementationsL( KBTEngPluginUid, iPluginInfoArray );
        }
        // Ignore the number of plug-ins left to load; the server state machine 
        // will handle this at a later stage.
    (void) LoadPluginL();
    TRACE_FUNC_EXIT
    }

TBool CBTEngSrvPluginMgr::FilterByEnterpriseDisablementModeL(TUid aUid) const
	{
	TRACE_FUNC_ENTRY
	TBool want = EFalse;
	switch ( BluetoothFeatures::EnterpriseEnablementL() )
		{
	case BluetoothFeatures::EDisabled:
		// In Disabled mode all plugins are filtered out.
		break;
	case BluetoothFeatures::EDataProfilesDisabled:
		// In 'privileged profiles only' mode we only allow the following.
		if ( 		aUid == TUid::Uid(0x1020897B) // audio (i.e. allow HSP, HFP and A2DP)
				||	aUid == TUid::Uid(0x10208979) // remote control (i.e. allow AVRCP) )
				||	aUid == TUid::Uid(0x2001E309) // HID
			)
			{
			want = ETrue;
			}
		break;
	case BluetoothFeatures::EEnabled:
		// In Enabled mode we do not filter plugins.
		want = ETrue;
		break;
	default:
		__ASSERT_DEBUG( NULL, PanicServer( EBTEngPanicCorruptSettings ) );
		break;
		}
	TRACE_INFO( ( _L( "[BTENG]\t returning want = %d" ), want ) )
	TRACE_FUNC_EXIT
	return want;
	}

// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
TInt CBTEngSrvPluginMgr::LoadPluginL()
    {
    TRACE_FUNC_ENTRY
    if( !iPluginInfoArray.Count() )
        {
            // All plug-ins have been loaded.
        return KErrNotFound;
        }

        // Simply pop the first info object, process it, and delete it.
        // There is no need to keep it after the plug-in has been constructed.
    CImplementationInformation* implInfo = iPluginInfoArray[ 0 ];
    iPluginInfoArray.Remove( 0 );
    CleanupStack::PushL( implInfo );
    TLex8 lex( implInfo->DataType() );
    TUint profile = (TInt) EBTProfileUndefined;
    TInt err = lex.Val( profile, EHex );
        // Check if the feature is allowed to be loaded
    if( !err && CheckFeatureEnabledL( profile ) && FilterByEnterpriseDisablementModeL(implInfo->ImplementationUid()) )
        {
        TRACE_INFO( ( _L( "[BTENG]\t loading profile 0x%04x" ), profile ) )
        TUid implUid = implInfo->ImplementationUid();
        CBTEngPlugin* plugin = CBTEngPlugin::NewL( implUid );
        CleanupStack::PushL( plugin );
        plugin->SetObserver( ( MBTEngPluginObserver* ) this );
        User::LeaveIfError( iPluginArray.Append( plugin ) );
        CleanupStack::Pop( plugin );
        }
    CleanupStack::PopAndDestroy( implInfo ); 
    TRACE_FUNC_RES( ( _L( "%d plug-ins left to load" ), iPluginInfoArray.Count() ) )
    return iPluginInfoArray.Count();
    }


// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
void CBTEngSrvPluginMgr::UnloadProfilePlugins()
    {
    TRACE_FUNC_ARG( ( _L( "[BTENG]\t unloading %d plug-ins" ), iPluginArray.Count() ) )

    // All plug-ins need to be unloaded at once. Otherwise it gets too
    // difficult to keep track in case of a power-on command during a 
    // power-off sequence.
    iPluginArray.ResetAndDestroy();

    REComSession::FinalClose();
    TRACE_FUNC_EXIT
    }


// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
void CBTEngSrvPluginMgr::LoadBTSapPluginL()
    {
    TRACE_FUNC_ENTRY

	// SAP is supported in neither Data Profiles Disabled nor Disabled mode.
    if ( BluetoothFeatures::EnterpriseEnablementL() != BluetoothFeatures::EEnabled )
        {
        TRACE_INFO( ( _L( "\tno we're not... Bluetooth is enterprise-IT-disabled" ) ) )
        User::Leave(KErrNotSupported);
        }
    
    if( CheckFeatureEnabledL( EBTProfileSAP ) )
        {
        TEComResolverParams params;
        TPtrC8 ptr( KEComBTSapPlugin );
        params.SetDataType( ptr );
        LoadProfilePluginsL( params );
        }
    TRACE_FUNC_EXIT
    }


// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
void CBTEngSrvPluginMgr::UnloadBTSapPlugin()
    {
    TRACE_FUNC_ENTRY
    TInt ret = GetFirstPluginOfProfileSupported( EBTProfileSAP );
    if( ret != KErrNotFound )
        {
        CBTEngPlugin* plugin = iPluginArray[ ret ];
        iPluginArray.Remove( ret );
        delete plugin;
        }
    
    TRACE_FUNC_EXIT
    }
    
// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
void CBTEngSrvPluginMgr::DisconnectProfile( TBTProfile aProfile )
    {
    TRACE_FUNC_ENTRY
    RBTDevAddrArray addrArray;
    TInt count = iPluginArray.Count();
    while( count )
        {
        count--;
        if( iPluginArray[ count ]->IsProfileSupported( aProfile ) )
            {
            iPluginArray[ count ]->GetConnections( addrArray, aProfile );
            for ( TInt i = 0;  i < addrArray.Count(); i++ )
                {
                iPluginArray[ count ]->Disconnect( addrArray[i], EBTDiscImmediate );
                }
            break;
            }
        }
    TRACE_FUNC_EXIT
    }


// ---------------------------------------------------------------------------
// From class MBTEngPluginObserver.
// ?implementation_description
// ---------------------------------------------------------------------------
//
void CBTEngSrvPluginMgr::ConnectComplete( const TBTDevAddr& aAddr, 
    TBTProfile aProfile, TInt aErr, RBTDevAddrArray* aConflicts )
    {
    TRACE_FUNC_ENTRY
        // Inform listeners of this event.
    (void) aProfile;
    iServer->iSessionIter.SetToFirst();
    CBTEngSrvSession* session = (CBTEngSrvSession*) iServer->iSessionIter++;
    while( session )
        {
        TRACE_INFO( ( _L( "[BTEng]\t Notifying session %d" ), (TInt) session ) )
        session->NotifyConnectionEvent( aAddr, EBTEngConnected, aConflicts, aErr );
        session = (CBTEngSrvSession*) iServer->iSessionIter++;
        }
    TRACE_FUNC_EXIT
    }


// ---------------------------------------------------------------------------
// From class MBTEngPluginObserver.
// ?implementation_description
// ---------------------------------------------------------------------------
//
void CBTEngSrvPluginMgr::DisconnectComplete( const TBTDevAddr& aAddr, 
    TBTProfile aProfile, TInt aErr )
    {
    TRACE_FUNC_ENTRY
        // Inform listeners of this event.
    (void) aProfile;
    iServer->iSessionIter.SetToFirst();
    CBTEngSrvSession* session = (CBTEngSrvSession*) iServer->iSessionIter++;
    while( session )
        {
        TRACE_INFO( ( _L( "[BTEng]\t Notifying session %d" ), (TInt) session ) )
        session->NotifyConnectionEvent( aAddr, EBTEngNotConnected, NULL, aErr );
        session = (CBTEngSrvSession*) iServer->iSessionIter++;
        }
    TRACE_FUNC_EXIT
    }

// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
TInt CBTEngSrvPluginMgr::Connect( const TBTDevAddr& aAddr, 
    const TBTDeviceClass& aDeviceClass )
    {
    TRACE_FUNC_ENTRY
    TRACE_BDADDR ( aAddr )
    TInt pindex = GetConnectablePluginIndex( aDeviceClass );    
    TRACE_INFO( ( _L( "[BTEng]\t The %dth of plugin in plugarray to connect" ), pindex ) )
    TInt err (KErrNotFound);
    if( pindex != KErrNotFound && iPluginArray.Count())
        {
        err = iPluginArray[ pindex ]->Connect( aAddr );
        }
    TRACE_FUNC_RES( ( _L( "result: %d" ), err ) )
    return err;
    }


// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
TInt CBTEngSrvPluginMgr::CancelConnect( const TBTDevAddr& aAddr )
    {
    TRACE_FUNC_ENTRY
    TInt err = KErrNotFound;
    TRACE_BDADDR(aAddr);
    for( TInt i = 0; i < iPluginArray.Count(); i++ )
        {
        TBTEngConnectionStatus status = iPluginArray[ i ]->IsConnected( aAddr );
        if( status == EBTEngConnecting || status == EBTEngConnected )
            {
            (void) iPluginArray[ i ]->CancelConnect( aAddr );
            err = KErrNone;
            break;
            }
        }
    TRACE_FUNC_RES( ( _L( "result: %d" ), err ) )
    return err;
    }


// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
TInt CBTEngSrvPluginMgr::Disconnect( const TBTDevAddr& aAddr, 
    TBTDisconnectType aDiscType )
    {
    TRACE_FUNC_ENTRY
    TRACE_BDADDR(aAddr);
    TInt err = KErrNotFound;
    for( TInt i = 0; i < iPluginArray.Count(); i++ )
        {
        // Should be ignored if the plug-in does not have 
        // a connection to the address.
        TBTEngConnectionStatus status = iPluginArray[ i ]->IsConnected( aAddr );
        if( status == EBTEngConnecting || status == EBTEngConnected )
            {            
            err = iPluginArray[ i ]->Disconnect( aAddr, aDiscType );
            if( err )
                {
                TRACE_INFO( ( _L( "[BTENG]  ERR! Disconnect failed, plugin index %d with err %d" ), i, err ) )
                }
            }
        }
    TRACE_FUNC_RES( ( _L( "result: %d" ), err ) )
    return err;
    }


// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
TBTEngConnectionStatus CBTEngSrvPluginMgr::IsDeviceConnected( const TBTDevAddr& aAddr )
    {
    TRACE_FUNC_ENTRY
    TRACE_BDADDR(aAddr);
    TBTEngConnectionStatus status = EBTEngNotConnected;
    for( TInt i = 0; i < iPluginArray.Count(); i++ )
        {
        status = iPluginArray[ i ]->IsConnected( aAddr );
        if( status == EBTEngConnecting || status == EBTEngConnected )
            {
            break;  // Just exit the loop here, we have a connection status.
            }
        }
    TRACE_FUNC_RES( ( _L( "result: %d" ), (TInt) status ) )
    return status;
    }


// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
TInt CBTEngSrvPluginMgr::GetConnectablePluginIndex( const TBTDeviceClass& aDeviceClass, const TBTDevAddr& aAddr )
    {
    TRACE_FUNC_ENTRY
    TInt plugindex( KErrNotFound );
    TRACE_BDADDR(aAddr);
    TRACE_INFO ( (_L("[BTENG] cod %b"), aDeviceClass.DeviceClass()))
    if( aAddr != TBTDevAddr() )
        {
        DoGetEirData( aAddr );
        }
    
    if( iUuidContainter.UUIDs().Count() > 0)
        {
        plugindex = GetConnectablePluginIndexByEir();
        }
    
    if ( plugindex == KErrNotFound )
        {
        TBTProfile profile = MapDeviceClassToProfile( aDeviceClass );
        if( profile == EBTProfileUndefined )
            {
            return plugindex;
            }
        
        plugindex = GetFirstPluginOfProfileSupported( profile );
        
        if ( !iPluginArray.Count() )
            {
            // In case BT is off and plugins are not loaded
            if ( profile == EBTProfileHFP || profile == EBTProfileA2DP )
                {
                plugindex = KErrNone; 
                }
            }
        }
    
    TRACE_FUNC_RES( ( _L( "result: %d" ), plugindex ) )
    return plugindex;
    }

// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
TInt CBTEngSrvPluginMgr::GetConnectedAddresses( RBTDevAddrArray& aAddrArray, 
    TBTProfile aProfile )
    {
    TRACE_FUNC_ENTRY    
    TInt ret = GetFirstPluginOfProfileSupported( aProfile );
    if( ret != KErrNotFound )
        {
        iPluginArray[ ret ]->GetConnections( aAddrArray, aProfile );
        ret = KErrNone;
        }
    TRACE_FUNC_RES( ( _L( "result: %d" ), ret ) )
    return ret;
    }

// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
TBool CBTEngSrvPluginMgr::CheckFeatureEnabledL( TInt aProfile )
    {
    TRACE_FUNC_ARG( ( _L( "requested feature: 0x%04x" ), aProfile ) )
        // By default, a feature is supported. This allowd features that do not
        // have a related feature flag to be loaded too.
    TBool supported = ETrue;
    if( aProfile == EBTProfileSAP )
        {
            // First check from central repository.
        CRepository* cenrep = CRepository::NewL( KCRUidBTEngPrivateSettings );
        TInt enabled = 0;
        TInt err = cenrep->Get( KBTSapEnabled, enabled );
        delete cenrep;
        if( err || !enabled )
            {
            return EFalse;
            }
        }

    TInt feature = MapProfileToFeature( aProfile );
    if( feature )
        {
            // Check from feature manager if this phone enables this feature.
        FeatureManager::InitializeLibL();
        supported = FeatureManager::FeatureSupported( feature );
        FeatureManager::UnInitializeLib();
        }
    TRACE_FUNC_RES( ( _L ( "result: %d" ), supported ) )
    return supported;
    }


// ---------------------------------------------------------------------------
// ?implementation_description
// ---------------------------------------------------------------------------
//
TBTProfile CBTEngSrvPluginMgr::MapDeviceClassToProfile( 
    const TBTDeviceClass& aDeviceClass )
    {
    TRACE_FUNC_ARG( ( _L( "Mapping CoD %b ..." ), aDeviceClass.DeviceClass() ) )
    // Could (should?) be done more dynamically or with some header file definition.
    // Right now these are the only known/possible ECOM plug-ins.
    TBTProfile profile = EBTProfileUndefined;
    if( aDeviceClass.MajorServiceClass() & EMajorServiceAudio )
        {
        profile = EBTProfileHFP;
        }
    else if( aDeviceClass.MajorServiceClass() & EMajorServiceRendering &&
            ( aDeviceClass.MajorDeviceClass() != EMajorDeviceImaging ) )
        // Printer or camera or other imaging device may set EMajorServiceRendering bit
        // as well as stereo audio device, so check EMajorDeviceImaging too.
        {
        profile = EBTProfileA2DP;
        }
    else if( aDeviceClass.MajorDeviceClass() == EMajorDevicePeripheral &&
            ( (aDeviceClass.MinorDeviceClass() & EMinorDevicePeripheralKeyboard) ||
              (aDeviceClass.MinorDeviceClass() & EMinorDevicePeripheralPointer) ) )
        {
        profile = EBTProfileHID;
        }
    else if( aDeviceClass.MajorDeviceClass() == EMajorDeviceLanAccessPoint )
        {
            // Mainly for testing now; PAN profile is a personal favorite.
        profile = EBTProfilePANU;
        }
    
    TRACE_FUNC_RES( ( _L( "... to profile 0x%04x." ), profile ) )
    return profile;
    }


// ---------------------------------------------------------------------------
// Maps a profile UUID to any Bluetooth-related pflatfom feature flag.
// ---------------------------------------------------------------------------
//
TInt CBTEngSrvPluginMgr::MapProfileToFeature( TInt aProfile )
    {
    TInt feature = 0;
    switch( aProfile )
        {
        case EBTProfileHSP:
        case EBTProfileHFP:
            {
            feature = KFeatureIdBtAudio;
            }
            break;
        case EBTProfileA2DP:
            {
            feature = KFeatureIdBtStereoAudio;
            }
            break;
        case EBTProfileSAP:
            {
            feature = KFeatureIdBtSap;
            }
            break;
        case EBTProfileDUN:
            {
            feature = KFeatureIdDialupNetworking;
            }
            break;
        case EBTProfileFAX:
            {
            feature = KFeatureIdBtFaxProfile;
            }
            break;
        case EBTProfilePANU:
        case EBTProfileNAP:
        case EBTProfileGN:
            {
            //feature = KFeatureIdBtPanProfile;
            feature = 0;// Testin'
            }
            break;
        case EBTProfileBIP:
            {
            feature = KFeatureIdBtImagingProfile;
            }
            break;
        case EBTProfileBPP:
            {
            feature = KFeatureIdBtPrintingProfile;
            }
            break;
        default:
            break;
        }
    TRACE_FUNC_RES( ( _L( "selected feature %d" ), feature ) )
    return feature;
    }

// ---------------------------------------------------------------------------
// Get Eir Data by hostResolver in Cache
// ---------------------------------------------------------------------------
//
TInt CBTEngSrvPluginMgr::DoGetEirData( const TBTDevAddr& aAddr )
    {
    TRACE_FUNC_ENTRY
    TInt err = KErrNone;
    iUuidContainter.Close();
    
    if( !iServer->iSocketServ.Handle() )
        {
        err = iServer->iSocketServ.Connect();
        }
    
    TProtocolDesc pInfo;
    if(err == KErrNone)
        {
        _LIT(KBTLinkManagerProtocol, "BTLinkManager");
        err = iServer->iSocketServ.FindProtocol( KBTLinkManagerProtocol(), pInfo );
        }
    if (err == KErrNone)
        {
        err = iHostResolver.Open(iServer->iSocketServ, pInfo.iAddrFamily, pInfo.iProtocol);
        }
    if(err == KErrNone)
        {
        iInquirySockAddr = TInquirySockAddr();
        iInquirySockAddr.SetBTAddr(aAddr);
        iInquirySockAddr.SetAction(KHostResCache | KHostResEir);
        
        TRequestStatus status;
        iHostResolver.GetByAddress(iInquirySockAddr, iNameEntry, status);
        User::WaitForRequest(status);
        err = status.Int();
        TRACE_FUNC_RES(( _L( "HostResolver GetByAddress status: %d" ), err))
        iHostResolver.Close();
        }
    if(err == KErrNone)
        {
        TBluetoothNameRecordWrapper eirWrapper( iNameEntry() );
        err = eirWrapper.GetServiceClassUuids( iUuidContainter );
        }
    
    TRACE_FUNC_EXIT
    return err;
    }

// ---------------------------------------------------------------------------
// Check if Service UUID is supported by some plugin.
// ---------------------------------------------------------------------------
//
TInt CBTEngSrvPluginMgr::GetConnectablePluginIndexByEir()
    {
    CBTEngPlugin::RProfileArray profiles;
    TInt count = iUuidContainter.UUIDs().Count();
    
    for( TInt u = 0; u < count; u++)
        {
        for( TInt i = 0; i < iPluginArray.Count(); i++ )
            {
            profiles.Reset();
            iPluginArray[ i ]->GetSupportedProfiles( profiles );
            for (TInt x = 0; x < profiles.Count(); x++)
                {
                if (iUuidContainter.UUIDs().At(u) == TUUID(profiles[x]))
                    {
                    profiles.Close();
    				TRACE_INFO( ( _L( "connectable plugin index %d" ), i ) )
                    return i;
                    }
                }
            }
        }
    profiles.Close();
    return KErrNotFound;
    }

// ---------------------------------------------------------------------------
// Internal utility function 
// ---------------------------------------------------------------------------
//
TInt CBTEngSrvPluginMgr::GetFirstPluginOfProfileSupported(TBTProfile aProfile )
    {
    TRACE_FUNC_ENTRY
    for( TInt i = 0; i < iPluginArray.Count(); i++ )
        {
        if( iPluginArray[ i ]->IsProfileSupported( aProfile ) )
            {
            return i;
            }
        }
    return KErrNotFound;
    }

// ---------------------------------------------------------------------------
//  Check if any audio connection exists. 
// ---------------------------------------------------------------------------
//
	
TBool CBTEngSrvPluginMgr::CheckAudioConnectionsL()
    {
    TRACE_FUNC_ENTRY
    TBool result = EFalse;
    RArray <TBTProfile> profiles;
	CleanupClosePushL( profiles );
    profiles.AppendL( EBTProfileHSP );
    profiles.AppendL( EBTProfileHFP );
    profiles.AppendL( EBTProfileA2DP );
    profiles.AppendL( EBTProfileAVRCP );
    
	RBTDevAddrArray addrArray;	
    for ( TInt i = 0; i < profiles.Count(); i++ )
        {
        GetConnectedAddresses( addrArray, profiles[i] );
        if ( addrArray.Count() > 0 )
            {
            result = ETrue;
            break;
            }
        addrArray.Reset();
        }
	addrArray.Close();
	
	CleanupStack::PopAndDestroy( &profiles );
    TRACE_FUNC_RES( ( _L( "result: %d" ), result ) )
    return result;
    }