diff -r 0ba996a9b75d -r 613943a21004 bluetoothengine/btnotif/btnotifsrv/src/btnotifoutgoingpairinghandler.cpp --- /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 +#include +#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 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 ); + } + + +