bluetoothengine/btnotif/btnotifsrv/src/btnotifoutgoingpairinghandler.cpp
author hgs
Tue, 19 Oct 2010 15:09:34 +0300
changeset 70 f5508c13dfe0
parent 57 5ebadcda06cb
permissions -rw-r--r--
201041

/*
* 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: Pairing handler for local device initiated pairing
*
*/

#include "btnotifoutgoingpairinghandler.h"
#include <btengconstants.h>
#include <btservices/btdevextension.h>
#include "btnotifsecuritymanager.h"
#include "bluetoothtrace.h"
#include "bluetoothnotification.h"
#include "btnotifserver.h"
#include "btnotifconnectiontracker.h"
#include "btnotificationmanager.h"

/**  Length of the default PIN. */
const TInt KDefaultHeadsetPinLength = 4;

/** Maximum repeated outgoing pairing attempt.
 *  if the pairing fails the UI specs says
 *  we can ask twice the user if he/she want 
 *  to retry pairing. 
 */
const TInt KMaxRepeatedPairingAttempt = 2;

enum TPairingStageId
    {
    /**
     * no pairing operation ongoing
     */
    ENoBonding = 0,
    
    /**
     * pair with dedicated bonding method
     */
    EDedicatedBonding = 200,
    
    /**
     * pair with general bonding by establishing L2CAP connection.
     */
    EGeneralBonding,  
    
    /**
     * delaying next pairing request for a while
     */
    EGeneralBondingRetryTimer,
    
    /**
     * The last pairing retry
     */
    EGeneralBondingRetry,
    
    /**
     * disconnecting physical link after pairing operation.
     * 
     * todo: not used yet.
     */
    EDisconnectLinkAfterBonding,
    };

/**  SDP PSM (used for pairing) */
const TInt KSDPPSM = 0x0001;

// Delay time to void Repeated Attempts on pairing
const TInt KGeneralBondingRetryDelayMicroSeconds = 5000000; // 5.0s

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

// ---------------------------------------------------------------------------
// C++ default constructor
// ---------------------------------------------------------------------------
//
CBTNotifOutgoingPairingHandler::CBTNotifOutgoingPairingHandler( CBTNotifSecurityManager& aParent, const TBTDevAddr& aAddr)
    :  CBTNotifBasePairingHandler( aParent, aAddr )
    {
    iDialogIsOrphan = EFalse;
    }

// ---------------------------------------------------------------------------
// Symbian 2nd-phase constructor
// ---------------------------------------------------------------------------
//
void CBTNotifOutgoingPairingHandler::ConstructL()
    {
    BaseConstructL();
    User::LeaveIfError( iTimer.CreateLocal() );
    iPairingAttempt = KMaxRepeatedPairingAttempt;
    }

// ---------------------------------------------------------------------------
// NewL
// ---------------------------------------------------------------------------
//
CBTNotifBasePairingHandler* CBTNotifOutgoingPairingHandler::NewL( CBTNotifSecurityManager& aParent, 
        const TBTDevAddr& aAddr )
    {
    CBTNotifOutgoingPairingHandler* self = new( ELeave ) CBTNotifOutgoingPairingHandler( aParent, aAddr );
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
    return self;
    }

// ---------------------------------------------------------------------------
// Destructor
// ---------------------------------------------------------------------------
//
CBTNotifOutgoingPairingHandler::~CBTNotifOutgoingPairingHandler()
    {
    if ( iActive ) 
        {
        iActive->Cancel();
        }
    iBondingSession.Close();
    iSocket.Close();
    iTimer.Close();
    if( iNotification && !iDialogIsOrphan)
        {
        // Clear the notification callback, we cannot receive them anymore.
        iNotification->RemoveObserver();
        iNotification->Close(); // Also dequeues the notification from the queue.
        iNotification = NULL;
        }
    if( iNotification && iDialogIsOrphan)
        {
        // Clear the notification callback, we cannot receive them anymore.
        iNotification->RemoveObserver();
        }
    }

// ---------------------------------------------------------------------------
// Simply deny the request as this is handing outgoing pairing
// ---------------------------------------------------------------------------
//
TInt CBTNotifOutgoingPairingHandler::ObserveIncomingPair( const TBTDevAddr& aAddr )
    {
    if(iAddr == aAddr)
        {
        return KErrNone;
        }
    else
        {
        return KErrServerBusy;
        }
    }

// ---------------------------------------------------------------------------
// Accept the request only this device is not busy with another pairing request.
// ---------------------------------------------------------------------------
//
void CBTNotifOutgoingPairingHandler::HandleOutgoingPairL( const TBTDevAddr& aAddr, TUint aCod )
    {
    BOstrace1(TRACE_DEBUG,DUMMY_DEVLIST,_L(" cod 0x%08x"), aCod );
    if ( iActive->IsActive() || aAddr != iAddr )
        {
        // we don't allow another pairing request.
        User::Leave( KErrServerBusy );
        }
    iAddr = aAddr;
    iCod = TBTDeviceClass( aCod );
    UnSetPairResult();
    iParent.UnpairDevice( iAddr );
    if ( CBtDevExtension::IsHeadset( iCod ) )
        {
        // If the devie is a headset, set to 0000 pin auto pairing
        iPairMode = EBTOutgoingHeadsetAutoPairing;
        }
    else
        {
        iPairMode = EBTOutgoingNoneHeadsetPairing;
        }
    DoPairingL();
    }

// ---------------------------------------------------------------------------
// Cancels an outstanding pair request by self-destruct
// ---------------------------------------------------------------------------
//
void CBTNotifOutgoingPairingHandler::CancelOutgoingPair()
    {
    BOstraceFunctionEntry0( DUMMY_DEVLIST );
    ShowPairingFailureDialog();
    iParent.RenewPairingHandler( NULL );
    }


// ---------------------------------------------------------------------------
// when phone initiated a pairing request towards a headset,
// Pin code 0000 is first tried.
// ---------------------------------------------------------------------------
//
void CBTNotifOutgoingPairingHandler::GetPinCode( 
        TBTPinCode& aPin, const TBTDevAddr& aAddr, TInt aMinPinLength )
    {
    BOstraceFunctionEntry0( DUMMY_DEVLIST );
    aPin().iLength = 0;
    if ( aMinPinLength <= KDefaultHeadsetPinLength 
            && aAddr == iAddr
            && iPairMode == EBTOutgoingHeadsetAutoPairing)
        {
        // if the pairing requires a stronger security level (indicated
        // by aMinPinLength), 
        // 0000 will not be supplied as it does not meet the security
        // requirements
        const TUint8 KZeroPinValue = '0';
        for (TInt i = 0; i < KDefaultHeadsetPinLength; ++i)
            {
                aPin().iPIN[i] = KZeroPinValue;
            }
        aPin().iLength = KDefaultHeadsetPinLength;
        }
    }

// ---------------------------------------------------------------------------
// Abort pairing handling, request the owner to destroy this.
// ---------------------------------------------------------------------------
//
void CBTNotifOutgoingPairingHandler::StopPairHandling( const TBTDevAddr& aAddr )
    {
    BOstraceFunctionEntry0( DUMMY_DEVLIST );
    if ( aAddr == iAddr )
        {
        iParent.OutgoingPairCompleted( KErrCancel );
        iParent.RenewPairingHandler( NULL );
        }
    }

// ---------------------------------------------------------------------------
// Pairing result will be received when pairing operation completes.
// ---------------------------------------------------------------------------
//
void CBTNotifOutgoingPairingHandler::DoHandlePairServerResult( TInt aResult )
    {
    BOstraceFunctionEntry0( DUMMY_DEVLIST );
	if (aResult == (KHCIErrorBase-EPairingNotAllowed))
		{
		// if EPairingNotAllowed is recieved then any further pairing attempts will fail
		// so don't attampt to pair
        iPairMode = EBTOutgoingPairNone;
        ShowPairingFailureDialog();
        iParent.OutgoingPairCompleted( aResult );
        iParent.RenewPairingHandler( NULL );
		}
    }

// ---------------------------------------------------------------------------
// Cancels possible outstanding pairing and notify user pair success.
// ---------------------------------------------------------------------------
//
void CBTNotifOutgoingPairingHandler::DoHandleRegistryNewPairedEvent( 
        const TBTNamelessDevice& aDev )
    {
    BOstraceFunctionEntry0( DUMMY_DEVLIST );
    TInt err( KErrNone );
    // If pairing was performed using Just Works mode, we set a 
    // UICookie to indicate that the device is successfully 
    // bonded so that this device will be listed in paired device view of
    // bluetooth application:
    if ( aDev.LinkKeyType() == ELinkKeyUnauthenticatedNonUpgradable )
        {
        BOstrace0(TRACE_DEBUG,DUMMY_DEVLIST,_L("[BTNOTIF] Outgoing Pairing, Just Works pairing"));
        err = iParent.AddUiCookieJustWorksPaired( aDev );
        }
    iActive->Cancel();
    SetPairResult( err ? err : KErrNone );
    if(err == KErrNone)
	    {
        // Trust the device
        if(iTrustDevice)
		    {
            iParent.TrustDevice(iAddr);
            }
        TRAP_IGNORE(ShowPairingResultNoteL(err));
        }
    iParent.OutgoingPairCompleted( err );
    iParent.RenewPairingHandler( NULL );
    }

// ---------------------------------------------------------------------------
// From class MBTNotifPairingAOObserver.
// Based on the result code, decides the next operation, either try pairing 
// with another mode, or complete pair request.
// ---------------------------------------------------------------------------
//
void CBTNotifOutgoingPairingHandler::RequestCompletedL( 
        CBtSimpleActive* aActive, TInt aStatus )
    {
    BOstraceFunctionEntry0( DUMMY_DEVLIST );
    BOstraceExt3(TRACE_DEBUG,DUMMY_DEVLIST,_L("reqid %d, status: %d, pair mode %d "), aActive->RequestId(), 
            aStatus, iPairMode);
    if( aActive->RequestId() == EDedicatedBonding && 
				( aStatus == KErrRemoteDeviceIndicatedNoBonding || 
					( aStatus && iPairMode != EBTOutgoingNoneHeadsetPairing && iPairMode != EBTOutgoingPairNone ) )   )
        {
        // try general pairing if the remote doesn't have dedicated bonding, or
        // pairing fails with a headset.
        DoPairingL();
        }
    else if ( aStatus && iPairMode == EBTOutgoingHeadsetAutoPairing )
        {
        iPairMode = EBTOutgoingHeadsetManualPairing;
        // auto pairing with headset failed, try to pair again with manual pin:
        BOstrace0(TRACE_DEBUG,DUMMY_DEVLIST,_L(" auto pairing failed, switch to manual pairing"));     
        DoPairingL();
        }
    else if ( aStatus && aActive->RequestId() == EGeneralBonding && 
              iPairMode == EBTOutgoingHeadsetManualPairing )
        {
        // pairing headset with manual pin failed, wait for a while and try again:
        iActive->SetRequestId( EGeneralBondingRetryTimer );
        iTimer.After( iActive->iStatus, KGeneralBondingRetryDelayMicroSeconds );
        iActive->GoActive();
        }
    else if( aActive->RequestId() == EGeneralBondingRetryTimer )
        {
        // try to pair headset again with manual pin again:
        DoPairingL();
        }
    else if ( aStatus )
        {
        // we only starts showing note if pairing failed.
        // For a successful pair, we must wait until registry has been updated.
        if ( !IsPairResultSet() )
            {
            SetPairResult( aStatus );
            }
        if ( aStatus <= KHCIErrorBase - EUnknownOpcode)
            {
            // retry pairing
            if(iPairingAttempt > 0)
                {
                if(aActive->RequestId() == EGeneralBondingRetry && iPairMode == EBTOutgoingHeadsetManualPairing)
                    {
                    // Headset pairing failed, reset and try again from auto pairing
                    iActive->SetRequestId(EGeneralBonding);
                    }
                iPairingAttempt --;
                ShowPairingRetryDialog();
                }
            else
                {
                iPairingAttempt --;
                iParent.OutgoingPairCompleted( aStatus );
                ShowPairingFailureDialog();
                iParent.RenewPairingHandler( NULL );
                }
             }
        else
            {
            iParent.OutgoingPairCompleted( aStatus );
            ShowPairingFailureDialog();
            iParent.RenewPairingHandler( NULL );
            }
        }
    }

// ---------------------------------------------------------------------------
// From class MBTEngActiveObserver.
// cancels an outstanding request according to the given id.
// ---------------------------------------------------------------------------
//
void CBTNotifOutgoingPairingHandler::CancelRequest( TInt aRequestId )
    {
    BOstraceFunctionEntry0( DUMMY_DEVLIST );
    switch ( aRequestId )
        {
        case EDedicatedBonding:
            {
            iBondingSession.Close();
            break;
            }
        case EGeneralBonding:
        case EGeneralBondingRetry:
            {
            iSocket.CancelConnect();
            iSocket.Close();
            break;
            }
        case EGeneralBondingRetryTimer:
            {
            iTimer.Cancel();
            break;
            }     
        }
    }

// ---------------------------------------------------------------------------
// From class MBTEngActiveObserver.
// Handles a leave in RequestCompleted by self-destructing.
// ---------------------------------------------------------------------------
//
void CBTNotifOutgoingPairingHandler::HandleError( 
        CBtSimpleActive* aActive, TInt aError )
    {
    BOstrace1(TRACE_DEBUG,DUMMY_DEVLIST,_L("error: %d"), aError );
    // Our RunL can actually not leave, so we should never reach here.
    (void) aActive;
    iParent.OutgoingPairCompleted( aError );
    iParent.RenewPairingHandler( NULL );
    }

// ---------------------------------------------------------------------------
// decide the next state and issue pair request
// ---------------------------------------------------------------------------
//
void CBTNotifOutgoingPairingHandler::DoPairingL()
    {
    BOstraceFunctionEntry0( DUMMY_DEVLIST );
    TPairingStageId currentMode = ( TPairingStageId ) iActive->RequestId();
    ASSERT( !iActive->IsActive() );
    TPairingStageId nextMode( EGeneralBonding );
    
    // if running BTv2.0 stack, dedicated bonding method 
    // is not available.
    if ( currentMode == ENoBonding && iParent.PairingServer() != NULL )
        {
        nextMode = EDedicatedBonding;
        }
    else if(currentMode == EGeneralBondingRetryTimer)
        {
        nextMode = EGeneralBondingRetry;
        }
    
    BOstraceExt2(TRACE_DEBUG,DUMMY_DEVLIST,_L("[BTNOTIF] CBTEngOtgPair::DoPairingL: bonding mode: pre %d, next %d"), currentMode, nextMode);
    
    iActive->SetRequestId( nextMode );
    if ( nextMode == EDedicatedBonding )
        {
        iBondingSession.Start( *iParent.PairingServer(), iAddr, iActive->RequestStatus() );          
        }
    else
        {
        TBTServiceSecurity sec;
        sec.SetAuthentication( ETrue );
        iSockAddr.SetBTAddr( iAddr );
        iSockAddr.SetPort(KSDPPSM);
        iSockAddr.SetSecurity( sec );    
        iSocket.Close();
        User::LeaveIfError( iSocket.Open( iParent.SocketServ(), KL2CAPDesC ) );
        iSocket.Connect( iSockAddr, iActive->RequestStatus() );
        }
    iActive->GoActive();
    BOstraceFunctionExit0( DUMMY_DEVLIST );
    }

// ---------------------------------------------------------------------------
// From class MBTNotificationResult.
// Handle a result from a user query.
// ---------------------------------------------------------------------------
//
void CBTNotifOutgoingPairingHandler::MBRDataReceived( CHbSymbianVariantMap& aData )
    {
    BOstraceFunctionEntry0( DUMMY_DEVLIST );
    (void) aData;
    BOstraceFunctionExit0( DUMMY_DEVLIST );
    }

// ---------------------------------------------------------------------------
// From class MBTNotificationResult.
// The notification is finished.
// ---------------------------------------------------------------------------
//
void CBTNotifOutgoingPairingHandler::MBRNotificationClosed( TInt aError, const TDesC8& aData )
    {
    BOstraceFunctionEntryExt( DUMMY_DEVLIST, this, aError );
    // First unregister from the notification, so we can already get the next one.
    iNotification->RemoveObserver();
    iNotification = NULL;
    TRAP_IGNORE( NotificationClosedL( aError, aData ) );
    BOstraceFunctionExit1( DUMMY_DEVLIST, this );
    }

// ---------------------------------------------------------------------------
// Get and configure a notification.
// ---------------------------------------------------------------------------
//
void CBTNotifOutgoingPairingHandler::PrepareNotificationL( TBluetoothDialogParams::TBTDialogType aType,
    TBTDialogResourceId aResourceId )
    {
    BOstraceFunctionEntry0( DUMMY_DEVLIST );
    iNotification = 
            iParent.ConnectionTracker().NotificationManager()->GetNotification();
    User::LeaveIfNull( iNotification ); // For OOM exception, leaves with KErrNoMemory
    iNotification->SetObserver( this );
    iNotification->SetNotificationType( aType, aResourceId );
    const CBtDevExtension* dev = iParent.BTDevRepository().Device(iAddr);
    if(dev)
        {
        User::LeaveIfError(iNotification->SetData( TBluetoothDeviceDialog::EDeviceName, dev->Alias()));
        }
    else
        {
        TBTDeviceName name;
        iAddr.GetReadable(name);
        User::LeaveIfError(iNotification->SetData( TBluetoothDeviceDialog::EDeviceName, name));
        NOTIF_NOTHANDLED( !err )            
        }
    iParent.ConnectionTracker().NotificationManager()->QueueNotificationL( iNotification);
    NOTIF_NOTHANDLED( !err )
    BOstraceFunctionExit0( DUMMY_DEVLIST );
    }

// ---------------------------------------------------------------------------
// The notification is finished, handle the result.
// ---------------------------------------------------------------------------
//
void CBTNotifOutgoingPairingHandler::NotificationClosedL( TInt aError, const TDesC8& aData )
    {
    BOstraceFunctionEntryExt( DUMMY_DEVLIST, this, aError );
    (void)aError;
    // Read the result.
    TPckgC<TBool> result( EFalse );
    result.Set( aData.Ptr(), result.Length() ); // Read the part containing the result
    // Set a pointer descriptor to capture the remaining data, if any.
    TPtrC8 dataPtr( aData.Mid( result.Length() ) );

    if(result() && iPairingAttempt >= 0)
        {
            HandleOutgoingPairL(iAddr,iCod.DeviceClass());
        }
    else
        {
            iPairingAttempt = KMaxRepeatedPairingAttempt; // reset the counter
            iParent.OutgoingPairCompleted( KErrCancel );
            iParent.RenewPairingHandler( NULL );
        }
    BOstraceFunctionExit1( DUMMY_DEVLIST, this );
    }

// ---------------------------------------------------------------------------
// Show a dialog to ask the user to retry the pairing
// ---------------------------------------------------------------------------
//
void CBTNotifOutgoingPairingHandler::ShowPairingRetryDialog()
    {
    PrepareNotificationL( TBluetoothDialogParams::EQuery, EPairingFailureRetry);
    }

// ---------------------------------------------------------------------------
// Show a dialog to tell the user pairing retry attempt failed
// ---------------------------------------------------------------------------
//
void CBTNotifOutgoingPairingHandler::ShowPairingFailureDialog()
    {
    if(!iPairingCancelledByUser)
        {
        iDialogIsOrphan = ETrue;
        PrepareNotificationL( TBluetoothDialogParams::EQuery, EPairingFailureOk );
        }
    }