bluetoothengine/btnotif/btnotifsrv/src/btnotifoutgoingpairinghandler.cpp
branchRCL_3
changeset 55 613943a21004
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetoothengine/btnotif/btnotifsrv/src/btnotifoutgoingpairinghandler.cpp	Tue Aug 31 15:25:10 2010 +0300
@@ -0,0 +1,553 @@
+/*
+* 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 )
+    {
+    }
+
+// ---------------------------------------------------------------------------
+// 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 )
+        {
+        // Clear the notification callback, we cannot receive them anymore.
+        iNotification->RemoveObserver();
+        iNotification->Close(); // Also dequeues the notification from the queue.
+        iNotification = NULL;
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// Simply deny the request as this is handing outgoing pairing
+// ---------------------------------------------------------------------------
+//
+TInt CBTNotifOutgoingPairingHandler::ObserveIncomingPair( const TBTDevAddr& aAddr )
+    {
+    (void)aAddr;
+    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," 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 );
+    iParent.RenewPairingHandler( NULL );
+    if( iNotification )
+        {
+        // Cancel the user query
+        // This will also unregister us from the notification.
+        TInt err = iNotification->Close();
+        NOTIF_NOTHANDLED( !err )
+        iNotification = 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 mmet 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 );
+        if( iNotification )
+            {
+            // Cancel the user query
+            // This will also unregister us from the notification.
+            TInt err = iNotification->Close();
+            NOTIF_NOTHANDLED( !err )
+            iNotification = 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;
+		}
+    }
+
+// ---------------------------------------------------------------------------
+// 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,"[BTNOTIF] Outgoing Pairing, Just Works pairing");
+        err = iParent.AddUiCookieJustWorksPaired( aDev );
+        }
+    iActive->Cancel();
+    SetPairResult( err ? err : KErrNone );
+    if(err == KErrNone){
+    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,"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," 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 )
+            {
+            // retry pairing
+            if(aStatus && 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 --;
+                ShowPairingFailureDialog();
+                }
+             }
+        }
+    }
+
+// ---------------------------------------------------------------------------
+// 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,"error: %d", aError );
+    // Our RunL can actually not leave, so we should never reach here.
+    (void) aActive;
+    iParent.OutgoingPairCompleted( aError );
+    iParent.RenewPairingHandler( NULL );
+    if( iNotification )
+        {
+        // Cancel the user query
+        // This will also unregister us from the notification.
+        TInt err = iNotification->Close();
+        NOTIF_NOTHANDLED( !err )
+        iNotification = 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,"[BTENG] 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 );
+    // 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()
+    {
+    PrepareNotificationL( TBluetoothDialogParams::EQuery, EPairingFailureOk );
+    }
+
+
+