diff -r 000000000000 -r f63038272f30 bluetoothengine/bthid/bthidserver/src/bthidserver.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bluetoothengine/bthid/bthidserver/src/bthidserver.cpp Mon Jan 18 20:28:57 2010 +0200 @@ -0,0 +1,1631 @@ +/* +* Copyright (c) 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: This is the implementation of application class + * +*/ + +#include +#include +#include +#include +//#include // for checking the MachineUID. also link to hal.lib +#include +//#include +#include +#include "bthidserver.h" +#include "bthidsession.h" +#include "bthidclientsrv.h" +#include "bthidconnection.h" +#include "socketlistener.h" +#include "bthidtypes.h" +#include "bthiddevice.h" +#include "hiddescriptorlist.h" +#include "hiddescriptor.h" +#include "bthidserver.pan" +#include "debug.h" +#include "debugconfig.h" +#include "hidgeneric.h" +#include "hidlayoutids.h" +#include "bthidPsKey.h" + + +#ifndef DBG +#ifdef _DEBUG +#define DBG(a) a +#else +#define DBG(a) +#endif +#endif + +//File store location +_LIT(KFileStore,"C:\\private\\2001E301\\bthiddevices.dat"); + +/** PubSub key read and write policies */ +_LIT_SECURITY_POLICY_C2( KBTHIDPSKeyReadPolicy, + ECapabilityLocalServices, ECapabilityReadDeviceData ); +_LIT_SECURITY_POLICY_C2( KBTHIDPSKeyWritePolicy, + ECapabilityLocalServices, ECapabilityWriteDeviceData ); + +// A version number to use when storing device information +// Only for future proofing. +const TInt KDataFileVersionNumber = 1; + +CBTHidServer::CBTHidServer() : + CGenericServer(CActive::EPriorityStandard) + { + // Implementation not required + } + +CBTHidServer::~CBTHidServer() + { + delete iShutDownTimer; + + delete iControlListener; + + delete iInterruptListener; + + delete iBTConnIndex; + + delete iMasterContIndex; // Causes deletion of iBTConnContainer + + if (iTempInterrupt) + { + iTempInterrupt->Close(); + delete iTempInterrupt; + } + + if (iTempControl) + { + iTempControl->Close(); + delete iTempControl; + } + + iFs.Close(); + iSocketServ.Close(); + + iReqs.ResetAndDestroy(); + + delete iGenHID; + + RProperty::Delete( KPSUidBthidSrv, KBTMouseCursorState ); + } + +CBTHidServer* CBTHidServer::NewL() + { + CBTHidServer* self = CBTHidServer::NewLC(); + CleanupStack::Pop(self); + return self; + } + +CBTHidServer* CBTHidServer::NewLC() + { + CBTHidServer* self = new (ELeave) CBTHidServer; + CleanupStack::PushL(self); + self->ConstructL(); + return self; + } + +const TInt KShutdownDelay = 5000000; + +void CBTHidServer::StartShutdownTimerIfNoSession() + { + if (!ConnectionCount() + && (!iShutDownTimer || !iShutDownTimer->IsActive())) + { + if (!iShutDownTimer)TRAP_IGNORE(iShutDownTimer = CPeriodic::NewL(CActive::EPriorityStandard)); + if (iShutDownTimer) + iShutDownTimer->Start(KShutdownDelay, 0, TCallBack( + CBTHidServer::TimerFired, this)); + TRACE_FUNC + } + } + +TInt CBTHidServer::TimerFired(TAny* /*aThis*/) + { + TRACE_STATIC_FUNC + CActiveScheduler::Stop(); + return KErrNone; + } + +void CBTHidServer::CancelShutdownTimer() + { + TRACE_FUNC + delete iShutDownTimer; + iShutDownTimer = NULL; + } + +void CBTHidServer::ConstructL() + { + + TRACE_INFO(_L("CBTHidServer::ConstructL()...")); + iMasterContIndex = CObjectConIx::NewL(); + + iBTConnContainer = iMasterContIndex->CreateL(); + + iBTConnIndex = CObjectIx::NewL(); + + // Connect to the file server + TRACE_INFO(_L("[BTHID]\tCBTHidServer::ConstructL(): Connecting to file server")); + User::LeaveIfError(iFs.Connect()); + + // Make the data storage path (if it doesn't exist) + // If we can't create it, we can still make connections. They just won't + // persist. + iFs.MkDirAll(KFileStore); + // Connect to the socket server. + TRACE_INFO(_L("[BTHID]\tCBTHidServer::ConstructL(): Connecting to socket server")); + User::LeaveIfError(iSocketServ.Connect()); + + // Create initial sockets to accept a connection on the control + // and interrupt channels + iTempControl = new (ELeave) RSocket; + iTempInterrupt = new (ELeave) RSocket; + + // Set the security required for incoming connections on the + // control and interrupt channels. This is handled in socket level now. + + // Create Socket listeners for the control and interrupt channel + // ETrue, authorisation from user for incoming connection is asked + iControlListener = CSocketListener::NewL(iSocketServ, KL2CAPHidControl, + *this, ETrue); + + //no authorisation needs to be asked, + //since it is asked during Control channel re-connection. + iInterruptListener = CSocketListener::NewL(iSocketServ, + KL2CAPHidInterrupt, *this, EFalse); + + // Request to accept connections into the sockets just created + TRACE_INFO(_L("[BTHID]\tCBTHidServer::ConstructL(): AcceptingConnections...")); + User::LeaveIfError(iControlListener->AcceptConnection(*iTempControl)); + User::LeaveIfError(iInterruptListener->AcceptConnection(*iTempInterrupt)); + + // Create the generic HID: + TRACE_INFO(_L("[BTHID]\tCBTHidServer::ConstructL(): Creating Generic HID")); + iGenHID = CGenericHid::NewL(this); + + // Load details of any virtually-cabled devices. + // Trap the error, but we can live with failure to load stored + // information. The file may be corrupt and we don't want this + // to prevent us using the application + TRACE_INFO(_L("[BTHID]\tCBTHidServer::ConstructL(): Loading virtually cabled devices")); + TRAPD( err, LoadVirtuallyCabledDevicesL(KFileStore) ); + if (KErrNone != err) + { + err = err; + TRACE_INFO(_L("[BTHID]\tCBTHidServer::ConstructL(): Loading virtually cabled devices FAILED")); + } + + TRACE_INFO(_L("[BTHID]\tCBTHidServer::ConstructL(): Starting the server")); + + User::LeaveIfError( RProperty::Define( KPSUidBthidSrv, + KBTMouseCursorState, + RProperty::EInt, + KBTHIDPSKeyReadPolicy, + KBTHIDPSKeyWritePolicy) ); + + StartL(KBTHidSrvName); + + iActiveState = EFalse; + + TRACE_INFO(_L("[BTHID]\tCBTHidServer::ConstructL(): Server Started.")); + + } + +CSession2* CBTHidServer::NewSessionL(const TVersion& aVersion, + const RMessage2& /*aMessage*/) const + { + // check we're the right version + if (!User::QueryVersionSupported(TVersion(KBTHIDServMajorVersionNumber, + KBTHIDServMinorVersionNumber, KBTHIDServBuildVersionNumber), + aVersion)) + { + User::Leave(KErrNotSupported); + } + const_cast (this)->CancelShutdownTimer(); + // make new session + return CBTHidServerSession::NewL(*const_cast (this)); + } + +void CBTHidServer::InformClientsOfStatusChange( + const CBTHidDevice& aDeviceDetails, TBTHidConnState aState) + { + TRACE_INFO( (_L("[BTHID]\tCBTHidServer::InformClientsOfStatusChange, state=%d"),aState) ); + if (aState == EBTDeviceConnected || aState == EBTDeviceLinkRestored + || aState == EBTDeviceLinkLost || aState == EBTDeviceDisconnected) + { + iLastUsedAddr = aDeviceDetails.iAddress; + iActiveState = ETrue; + } + else + { + iActiveState = EFalse; + } + InformStatusChange(aDeviceDetails.iAddress, aState); + GlobalNotify(aDeviceDetails.iAddress, aState); + } + +void CBTHidServer::InformStatusChange(const TBTDevAddr& aAddress, + TBTHidConnState aState) + { + TRACE_INFO( (_L("[BTHID]\tCBTHidServer::InformStatusChange, state=%d"),aState) ); + THIDStateUpdateBuf updateBuf; + THIDStateUpdate& update = updateBuf(); + + update.iDeviceAddress = aAddress; + update.iState = aState; + + // Send to all clients + iSessionIter.SetToFirst(); + for (;;) + { + CBTHidServerSession* session; + session = reinterpret_cast (iSessionIter++); + if (!session) + { + break; + } + + session->InformStatusChange(updateBuf); + } + } + +void CBTHidServer::GlobalNotify(const TBTDevAddr& aDeviceAddr, + TBTHidConnState aState) + { + switch (aState) + { + case EBTDeviceLinkRestored: + { + HandleAsyncRequest(aDeviceAddr, EBTConnected); + break; + } + + case EBTDeviceLinkLost: + case EBTDeviceDisconnected: + case EBTDeviceUnplugged: + { + HandleAsyncRequest(aDeviceAddr, EBTDisconnected); + break; + } + default: + //No need to bother + break; + } + } + +TInt CBTHidServer::HandleAsyncRequest(const TBTDevAddr& aDeviceAddr, + TInt aNote) + { + TRAPD(err, HandleAsyncRequestL(aDeviceAddr, aNote)); + return err; + } + +void CBTHidServer::HandleAsyncRequestL(const TBTDevAddr& aDeviceAddr, + TInt aNote) + { + CBTHidNotifierHelper* notifier = CBTHidNotifierHelper::NewL(*this, aNote, aDeviceAddr); + CleanupStack::PushL(notifier); + + iReqs.AppendL(notifier); + CleanupStack::Pop(notifier); + + if (iReqs.Count() == 1) // only display our notifier if there's nothing already showing + { + notifier->Start(); + } + } + +void CBTHidServer::NotifierRequestCompleted() + { + delete iReqs[0]; + iReqs.Remove(0); + + if (iReqs.Count()) + { + iReqs[0]->Start(); + } + } + +void CBTHidServer::GenericHIDConnectL(CBTHidConnection* aConnection, + TBool aStartDriver) + { + TRACE_INFO(_L("[BTHID]\tCBTHidServer::GenericHIDConnectL")); + CBTHidDevice& devDetails = aConnection->DeviceDetails(); + + // Search for the first report descriptor and give this + // to Generic HID + TBool foundRepDesc = EFalse; + TInt i = 0; + while ((i < devDetails.iDescList->DescriptorCount()) && (!foundRepDesc)) + { + // Get the next descriptor. + const CHidDescriptor& desc = + (*(aConnection->DeviceDetails().iDescList))[i]; + + if (desc.DescriptorType() == CHidDescriptor::EReportDescriptor) + { + foundRepDesc = ETrue; + User::LeaveIfError(iGenHID->ConnectedL(aConnection->ConnID(), + desc.RawData())); + + // Try to start the driver if required. + if (aStartDriver) + { + User::LeaveIfError(iGenHID->DriverActive( + aConnection->ConnID(), CHidTransport::EActive)); + } + } + + i++; + } + + // If we didn't find a report descriptor, the device information is corrupt + if (!foundRepDesc) + { + User::Leave(KErrCorrupt); + } + } + +void CBTHidServer::IncrementSessions() + { + iSessionCount++; + CancelShutdownTimer(); + } + +void CBTHidServer::DecrementSessions() + { + iSessionCount--; + __ASSERT_DEBUG(iSessionCount >= 0, PanicServer(EMainSchedulerError)); + + if (iSessionCount <= 0) + { + iSessionCount = 0; + StartShutdownTimerIfNoSession(); + } + } + +TInt CBTHidServer::RunError(TInt aError) + { + if (aError == KErrBadDescriptor) + { + // A bad descriptor error implies a badly programmed client, + // so panic it; + // otherwise report the error to the client + PanicClient(Message(), EBadDescriptor); + } + else + { + Message().Complete(aError); + } + + // The leave will result in an early return from CServer::RunL(), skipping + // the call to request another message. So do that now in order to keep the + // server running. + ReStart(); + + return KErrNone; // handled the error fully + } + +void CBTHidServer::PanicClient(const RMessage2& aMessage, TInt aPanic) + { + TRACE_INFO( (_L("[BTHID]\tCBTHidServer::PanicClient(%d)"), aPanic) ); + aMessage.Panic(KBTHIDServer, aPanic); + } + +void CBTHidServer::PanicServer(TInt aPanic) + { + TRACE_INFO( (_L("[BTHID]\tCBTHidServer::PanicServer( %d )"), aPanic) ); + User::Panic(KBTHIDServer, aPanic); + } + +void CBTHidServer::ShutdownListeners(TInt aError) + { + // Shutdown listeners and close accepting sockets + TRACE_INFO( (_L("[BTHID]\tCBTHidServer::ShutdownListeners(%d)"), aError) ) + + (void) aError; + + iControlListener->Cancel(); + iInterruptListener->Cancel(); + + if (iTempInterrupt) + { + iTempInterrupt->Close(); + } + + if (iTempControl) + { + iTempControl->Close(); + } + } + +TUint CBTHidServer::ConnectionCount() + { + return iBTConnContainer->Count(); + } + +CBTHidDevice& CBTHidServer::ConnectionDetailsL(TInt aConnID) + { + CBTHidConnection *connection = IdentifyConnectionL(aConnID); + + return connection->DeviceDetails(); + } + +TBTEngConnectionStatus CBTHidServer::ConnectStatus(const TBTDevAddr& aAddress) + { + TInt i = 0; + TBool foundItem = EFalse; + TBTEngConnectionStatus retVal = EBTEngNotConnected; + TInt BTConnectionObjCount = iBTConnContainer->Count(); + + TRACE_INFO(_L("[BTHID]\tCBTHidServer::ConnectStatus()")); + while ((i < BTConnectionObjCount) && (!foundItem)) + { + CBTHidConnection *connection = + static_cast ((*iBTConnContainer)[i]); + + if (connection) + { + CBTHidDevice& devDetails = connection->DeviceDetails(); + + if (devDetails.iAddress == aAddress) + { + foundItem = ETrue; + TBTConnectionState HidConnectionStatus = + connection->ConnectStatus(); + if (EFirstConnection == HidConnectionStatus || EConnecting + == HidConnectionStatus || EHIDReconnecting + == HidConnectionStatus || EHostReconnecting + == HidConnectionStatus) + { + retVal = EBTEngConnecting; + } + if (EConnected == HidConnectionStatus) + { + retVal = EBTEngConnected; + } + } + } + + i++; + } + + return retVal; + } + +TBool CBTHidServer::GetConnectionAddress(TDes8& aAddressBuf) + { + TInt i = 0; + TBool retVal = EFalse; + TInt BTConnectionObjCount = iBTConnContainer->Count(); + TRACE_INFO(_L("[BTHID]\tCBTHidServer::IsAllowToConnect()")); + while ((i < BTConnectionObjCount)) + { + CBTHidConnection *connection = + static_cast ((*iBTConnContainer)[i]); + + if (connection) + { + CBTHidDevice& devDetails = connection->DeviceDetails(); + if (connection->IsConnected()) + { + retVal = ETrue; + aAddressBuf.Append((devDetails.iAddress).Des()); + if (aAddressBuf.Length() == KBTDevAddrSize * 2) + break; + } + } + i++; + } + + return retVal; + } + +TBool CBTHidServer::IsAllowToConnectFromServerSide(TUint aDeviceSubClass) + { + TInt i = 0; + TBool retVal = ETrue; + TInt BTConnectionObjCount = iBTConnContainer->Count(); + + TRACE_INFO(_L("[BTHID]\tCBTHidServer::IsAllowToConnectFromServerSide()")); + while ((i < BTConnectionObjCount) && retVal) + { + CBTHidConnection *connection = + static_cast ((*iBTConnContainer)[i]); + + if (connection) + { + CBTHidDevice& devDetails = connection->DeviceDetails(); + TBTConnectionState HidConnectionStatus = + connection->ConnectStatus(); + if (connection->IsConnected() || HidConnectionStatus + == EHIDReconnecting) + { + if ((IsKeyboard(aDeviceSubClass) && IsKeyboard( + devDetails.iDeviceSubClass)) || (IsPointer( + aDeviceSubClass) && IsPointer( + devDetails.iDeviceSubClass))) + { + retVal = EFalse; + iConflictAddr = devDetails.iAddress; + } + } + } + i++; + } + + return retVal; + } + +TBool CBTHidServer::IsKeyboard(TUint aDeviceSubClass) + { + TUint deviceSubClass = aDeviceSubClass; + TBool retVal = EFalse; + if ((deviceSubClass >> 2) & EMinorDevicePeripheralKeyboard) + { + retVal = ETrue; + } + return retVal; + } + +TBool CBTHidServer::IsPointer(TUint aDeviceSubClass) + { + TUint deviceSubClass = aDeviceSubClass; + TBool retVal = EFalse; + if ((deviceSubClass >> 2) & EMinorDevicePeripheralPointer) + { + retVal = ETrue; + } + return retVal; + } + +TBool CBTHidServer::IsAllowToConnectFromClientSide(TBTDevAddr aDevAddr) + { + TInt i = 0; + TBool retVal = ETrue; + TInt BTConnectionObjCount = iBTConnContainer->Count(); + + TUint deviceSubClass = GetDeviceSubClass(aDevAddr); + TRACE_INFO(_L("[BTHID]\tCBTHidServer::IsAllowToConnectFromClientSide()")); + while ((i < BTConnectionObjCount) && retVal) + { + CBTHidConnection *connection = + static_cast ((*iBTConnContainer)[i]); + + if (connection) + { + CBTHidDevice& devDetails = connection->DeviceDetails(); + TBTConnectionState HidConnectionStatus = + connection->ConnectStatus(); + if (connection->IsConnected() || HidConnectionStatus + == EConnecting) + { + if (devDetails.iAddress != aDevAddr) + { + if ((IsKeyboard(deviceSubClass) && IsKeyboard( + devDetails.iDeviceSubClass)) || (IsPointer( + deviceSubClass) && IsPointer( + devDetails.iDeviceSubClass))) + { + retVal = EFalse; + iConflictAddr = devDetails.iAddress; + } + } + } + } + i++; + } + + return retVal; + } + +TUint CBTHidServer::GetDeviceSubClass(TBTDevAddr aDevAddr) + { + TInt i = 0; + TUint deviceSubClass = 0; + TInt BTConnectionObjCount = iBTConnContainer->Count(); + + TRACE_INFO(_L("[BTHID]\tCBTHidServer::GetMinorDeviceClass()")); + while (i < BTConnectionObjCount) + { + CBTHidConnection *connection = + static_cast ((*iBTConnContainer)[i]); + + if (connection) + { + CBTHidDevice& devDetails = connection->DeviceDetails(); + + if (devDetails.iAddress == aDevAddr) + { + deviceSubClass = devDetails.iDeviceSubClass; + } + } + i++; + } + return deviceSubClass; + } + +TBTDevAddr CBTHidServer::ConflictAddress() + { + return iConflictAddr; + } + +void CBTHidServer::CleanOldConnection(TInt aConnID) + { + TInt i = 0; + TBTDevAddr currentAddr; + TRACE_INFO( (_L("[BTHID]\tCBTHidServer::CleanOldConnection() aConnID[%d]"), aConnID) ); + CBTHidConnection* connection = + static_cast(iBTConnIndex->At(aConnID)); + TRACE_INFO( (_L("[BTHID]\tCBTHidServer::CleanOldConnection() aConnID[%d]"), aConnID) ); + __ASSERT_ALWAYS(connection, PanicServer(EInvalidHandle)); + + if (connection) + { + TRACE_INFO( _L("[BTHID]\tCBTHidServer::CleanOldConnection() if ") ); + CBTHidDevice& currentDetails = connection->DeviceDetails(); + currentAddr = currentDetails.iAddress; + TRACE_INFO( _L("[BTHID]\tCBTHidServer::CleanOldConnection() if 1") ); + while (i < iBTConnContainer->Count()) + { + TRACE_INFO( (_L("[BTHID]\tCBTHidServer::CleanOldConnection() i = [%d]"), i) ); + connection = static_cast ((*iBTConnContainer)[i]); + + if (connection) + { + CBTHidDevice& devDetails = connection->DeviceDetails(); + if (devDetails.iAddress == currentAddr && !(connection->IsConnected())) + { + TRACE_INFO( _L("[BTHID]\tCBTHidServer::CleanOldConnection() if 2") ); + iGenHID->Disconnected(connection->ConnID()); + iBTConnIndex->Remove(connection->ConnID()); + } + } + i++; + } + } + + return; + } + +TInt CBTHidServer::NewConnectionL() + { + TRACE_INFO(_L("[BTHID]\tCBTHidServer::NewConnectionL")); + CBTHidConnection *connection = CBTHidConnection::NewLC(iSocketServ, + *this, EConnecting); + // Add to the connection container object. + iBTConnContainer->AddL(connection); + CleanupStack::Pop(); // connection + + // Now add the object to the index to get an id. + // We can't let this just leave since we have already inserted the + // connection object into the container. + TInt id = 0; + TRAPD( res, + id = iBTConnIndex->AddL(connection); + connection->SetConnID(id); + ) + + if (res != KErrNone) + { + // Couldn't make an index entry. + // Close the connection object, causing it to be removed from the + // container + connection->Close(); + User::Leave(res); + } + + return id; + } + +void CBTHidServer::DoFirstConnectionL(TInt aConnID) + { + TRACE_INFO(_L("[BTHID]\tCBTHidServer::DoFirstConnectionL")); + CBTHidConnection *connection = IdentifyConnectionL(aConnID); + + CBTHidDevice &devDetails = ConnectionDetailsL(aConnID); + DBG(TUint DeviceClass = devDetails.iDeviceSubClass; + + DBG(RDebug::Print(_L("[BTHID]\tCBTHidServer::DoFirstConnectionL iDeviceSubClass = %d"), DeviceClass)); + ) + + + + if (!IsAllowToConnectFromServerSide(devDetails.iDeviceSubClass)) + { + User::Leave(KErrAlreadyExists); + } + + connection->ConnectL(); + } + +void CBTHidServer::DeleteNewConnection(TInt aConnID) + { + TRACE_INFO(_L("[BTHID]\tCBTHidServer::DeleteNewConnection")); + iBTConnIndex->Remove(aConnID); + } + +/*Asks the server to disconnect (virtual cable unplug) a device totally, + * remove the connection entry from the connection container. + */ +void CBTHidServer::CloseBluetoothConnection(const TBTDevAddr& aAddress) + + { + TInt i = 0; + TBool foundItem = EFalse; + + TRACE_INFO(_L("[BTHID]\tCBTHidServer::CloseBluetoothConnection")); + while ((i < iBTConnContainer->Count()) && (!foundItem)) + { + CBTHidConnection *connection = + static_cast ((*iBTConnContainer)[i]); + + CBTHidDevice& devDetails = connection->DeviceDetails(); + + if (devDetails.iAddress == aAddress) + { + foundItem = ETrue; + // Inform the Generic HID of the disconnection. + iGenHID->Disconnected(connection->ConnID()); + + // Get it to disconnect if its connected. + connection->Disconnect(); + + // Delete the connection object. + iBTConnIndex->Remove(connection->ConnID()); + + // Update the stored devices, as we could have power off + // and no clean shutdown. + // Use the non-leaving version. + StoreVirtuallyCabledDevices(KFileStore); + } + + i++; + } + + } + +/*Asks the server to disconnect all the devices totally, + * remove the connection entries from the connection container. + */ +void CBTHidServer::CloseAllBluetoothConnection() + { + TInt i = 0; + + TRACE_INFO(_L("[BTHID]\tCBTHidServer::CloseAllBluetoothConnection")); + + while (i < iBTConnContainer->Count()) + { + CBTHidConnection *connection = + static_cast ((*iBTConnContainer)[i]); + if (connection) + { + if (connection->IsConnected()) + { + // Inform the Generic HID of the disconnection. + iGenHID->Disconnected(connection->ConnID()); + + // Get it to disconnect if its connected. + connection->Disconnect(); + + // Delete the connection object. + iBTConnIndex->Remove(connection->ConnID()); + + // Update the stored devices, as we could have power off + // and no clean shutdown. + // Use the non-leaving version. + StoreVirtuallyCabledDevices(KFileStore); + } + } + + i++; + } + } + +void CBTHidServer::DisconnectDeviceL(const TBTDevAddr& aAddress) + { + TInt i = 0; + TBool foundItem = EFalse; + + TRACE_INFO(_L("[BTHID]\tCBTHidServer::DisconnectDeviceL")); + while ((i < iBTConnContainer->Count()) && (!foundItem)) + { + CBTHidConnection *connection = + static_cast ((*iBTConnContainer)[i]); + + CBTHidDevice& devDetails = connection->DeviceDetails(); + + if (devDetails.iAddress == aAddress) + { + foundItem = ETrue; + + // Drop the bluetooth connection. + connection->DropConnection(); + + // Stop the driver. + iGenHID->DriverActive(connection->ConnID(), + CHidTransport::ESuspend); + + InformClientsOfStatusChange(connection->DeviceDetails(), + EBTDeviceDisconnected); + //Microsoft Keyboard & other "Unsecure" devices + CheckAndSetControlListenerSecurityL( + connection->DeviceDetails().iUseSecurity); + } + + i++; + } + } + +void CBTHidServer::DisconnectAllDeviceL() + { + TInt i = 0; + + TRACE_INFO(_L("[BTHID]\tCBTHidServer::DisconnectAllDeviceL")); + while (i < iBTConnContainer->Count()) + { + CBTHidConnection *connection = + static_cast ((*iBTConnContainer)[i]); + + if (connection) + { + if (connection->IsConnected()) + { + CBTHidDevice& devDetails = connection->DeviceDetails(); + + // Drop the bluetooth connection. + connection->DropConnection(); + + // Stop the driver. + iGenHID->DriverActive(connection->ConnID(), + CHidTransport::ESuspend); + + InformClientsOfStatusChange(devDetails, + EBTDeviceDisconnected); + //Microsoft Keyboard & other "Unsecure" devices + CheckAndSetControlListenerSecurityL( + devDetails.iUseSecurity); + } + + i++; + } + } + } + +void CBTHidServer::CheckAndSetControlListenerSecurityL(TBool aSec) + { + + TRACE_INFO( (_L("[BTHID]\tCBTHidServer::CheckAndSetControlListenerSecurityL(%d)"), aSec) ); + + //Checking for Microsoft Keyboard & other "Unsecure" devices + + if (!aSec) //Security is set on in Constructor of listener. This overrides that setting. + { + delete iControlListener; + iControlListener = NULL; + if (iTempControl) + iTempControl->Close(); + iControlListener = CSocketListener::NewL(iSocketServ, + KL2CAPHidControl, *this, EFalse); //iAuthorisationFlag); We need authorisation, unless otherwise stated. + User::LeaveIfError(iControlListener->AcceptConnection(*iTempControl)); + } + + } + +// from MBTConnectionObserver +void CBTHidServer::HandleControlData(TInt aConnID, const TDesC8& aBuffer) + { + iGenHID->DataIn(aConnID, CHidTransport::EHidChannelCtrl, aBuffer); + } + +void CBTHidServer::HandleCommandAck(TInt aConnID, TInt aStatus) + { + iGenHID->CommandResult(aConnID, aStatus); + } + +void CBTHidServer::HandleInterruptData(TInt aConnID, const TDesC8& aBuffer) + { + iGenHID->DataIn(aConnID, CHidTransport::EHidChannelInt, aBuffer); + } + +void CBTHidServer::FirstTimeConnectionComplete(TInt aConnID, TInt aStatus) + { + TRACE_INFO( (_L("[BTHID]\tCBTHidServer::FirstTimeConnectionComplete(%d)"), aStatus)); + TInt error = aStatus; + + CBTHidConnection* connection = + static_cast (iBTConnIndex->At(aConnID)); + __ASSERT_ALWAYS(connection, PanicServer(EInvalidHandle)); + + if (error == KErrNone) + { + TBool genHidConnected = EFalse; + + TRAP( error, + // Inform the Generic HID of the Connection + GenericHIDConnectL(connection, ETrue); + + // Record that we got as far as informing the Generic HID. + genHidConnected = ETrue; + + // Try to start monitoring the channels. + connection->StartMonitoringChannelsL(); + ) + + if (error != KErrNone) + { + // If we informed the Generic HID of the connection, then + // we must also disconnect. + if (genHidConnected) + { + iGenHID->Disconnected(aConnID); + } + + // Delete the connection object. + iBTConnIndex->Remove(aConnID); + } + else + { + // Update the stored devices, as we could have power off + // and no clean shutdown. + // Use the non-leaving version. + TRACE_INFO( _L("[BTHID]\tCBTHidServer::FirstTimeConnectionComplete() before CleanOldConnection") ); + CleanOldConnection(aConnID); + TRACE_INFO( _L("[BTHID]\tCBTHidServer::FirstTimeConnectionComplete() after CleanOldConnection") ); + StoreVirtuallyCabledDevices(KFileStore); + } + } + else + { + iBTConnIndex->Remove(aConnID); + } + + // Report the connection result to the sessions. + iSessionIter.SetToFirst(); + for (;;) + { + CBTHidServerSession* session; + session = reinterpret_cast (iSessionIter++); + if (!session) + { + break; + } + + session->InformConnectionResult(aConnID, error); + } + } + +void CBTHidServer::LinkLost(TInt aConnID) + { + TRACE_INFO( (_L("[BTHID]\tCBTHidServer::LinkLost(%d)"), aConnID)); + // Stop the driver. + iGenHID->DriverActive(aConnID, CHidTransport::ESuspend); + + CBTHidConnection* connection = + static_cast (iBTConnIndex->At(aConnID)); + __ASSERT_ALWAYS(connection, PanicServer(EInvalidHandle)); + + // Inform clients of the change in status of this connection. + InformClientsOfStatusChange(connection->DeviceDetails(), + EBTDeviceLinkLost); + } + +void CBTHidServer::LinkRestored(TInt aConnID) + { + CBTHidConnection* connection = + static_cast (iBTConnIndex->At(aConnID)); + __ASSERT_ALWAYS(connection, PanicServer(EInvalidHandle)); + + // Inform the Generic HID of the reconnection + TInt error = iGenHID->DriverActive(aConnID, CHidTransport::EActive); + + // If there was no error, try to start monitoring the channels. + if (error == KErrNone) + { + // Try to start monitoring channels. + TRAP( error, connection->StartMonitoringChannelsL(); ) + } + + // Report new connection status. + if (error == KErrNone) + { + InformClientsOfStatusChange(connection->DeviceDetails(), + EBTDeviceLinkRestored); + } + else + { + InformClientsOfStatusChange(connection->DeviceDetails(), + EBTDeviceUnplugged); + // Inform the Generic HID of the disconnection. + iGenHID->Disconnected(aConnID); + // Delete the connection object. + iBTConnIndex->Remove(aConnID); + + // Update the stored devices, as we could have power off + // and no clean shutdown. + // Use the non-leaving version. + StoreVirtuallyCabledDevices(KFileStore); + } + } + +void CBTHidServer::Disconnected(TInt aConnID) + { + CBTHidConnection *connection = + static_cast (iBTConnIndex->At(aConnID)); + __ASSERT_ALWAYS(connection, PanicServer(EInvalidHandle)); + + // Stop the driver. + iGenHID->DriverActive(aConnID, CHidTransport::ESuspend); + + // Report new connection status. + InformClientsOfStatusChange(connection->DeviceDetails(), + EBTDeviceDisconnected); + + //Check if no security is needed for listening socket + //Microsoft Keyboard & other "Unsecure" devices need this. + + // possible leave is sign of severe problems in BT stack. So no reason to handle leave. + TRAP_IGNORE( CheckAndSetControlListenerSecurityL(connection->DeviceDetails().iUseSecurity) ); + + } + +void CBTHidServer::Unplugged(TInt aConnID) + { + CBTHidConnection *connection = + static_cast (iBTConnIndex->At(aConnID)); + __ASSERT_ALWAYS(connection, PanicServer(EInvalidHandle)); + + // Report new connection status. + InformClientsOfStatusChange(connection->DeviceDetails(), + EBTDeviceUnplugged); + + // Inform the Generic HID of the disconnection. + iGenHID->Disconnected(aConnID); + + iBTConnIndex->Remove(aConnID); + + // Update the stored devices, as we could have power off + // and no clean shutdown. + // Use the non-leaving version. + StoreVirtuallyCabledDevices(KFileStore); + } + +//from MListenerObserver +void CBTHidServer::SocketAccepted(TUint aPort, TInt aErrorCode) + { + TBTSockAddr sockAddr; + TBTDevAddr devAddr; + + TRACE_INFO( (_L("[BTHID]\tCBTHidServer::SocketAccepted, port=%d, error code=%d"), aPort, aErrorCode) ); + + // Check error code + if (aErrorCode != KErrNone) + { + // If we do get an error there isn't much we can about it. + // Just tidy up. + + ShutdownListeners(aErrorCode); + } + else + { + TInt i = 0; + TInt count = iBTConnContainer->Count(); + TInt err = KErrNone; + + // Check which port has accepted a connection + switch (aPort) + { + // The HID Control Channel + case KL2CAPHidControl: + // Get the BT address of the device that has connected + iTempControl->RemoteName(sockAddr); + devAddr = sockAddr.BTAddr(); + if (IsAllowToConnectFromClientSide(devAddr)) + { + while ((i < count) && (iTempControl)) + { + CBTHidConnection + * connection = + static_cast ((*iBTConnContainer)[i]); + __ASSERT_ALWAYS(connection, PanicServer(EInvalidHandle)); + connection->OfferControlSocket(devAddr, iTempControl); + i++; + } + + } + else + { + InformStatusChange(devAddr, EBTDeviceAnotherExist); + } + + // The next socket to accept into + if (iTempControl) + { + // Reuse this socket + iTempControl->Close(); + err = KErrNone; + } + else + { + // Socket ownership has been transferred so create a new + // socket + //TRAP( err, iTempControl = new (ELeave) RSocket; ) + iTempControl = new RSocket; + } + + // Created a socket to accept into so accept next connection + if (err == KErrNone) + { + err = iControlListener->AcceptConnection(*iTempControl); + } + + // If we failed to allocate a new RSocket or failed + // in the AcceptConnectionL call it means we can no longer + // accept connections from a device. + if (err != KErrNone) + { + TRACE_INFO(_L("[BTHID]\tCBTHidServer::SocketAccepted, control channel failed, shutdown listener")); + ShutdownListeners(err); + } + + break; + + // The HID Interrupt Channel + case KL2CAPHidInterrupt: + // Get the BT address of the device that has connected + iTempInterrupt->RemoteName(sockAddr); + devAddr = sockAddr.BTAddr(); + if (IsAllowToConnectFromClientSide(devAddr)) + { + while ((i < count) && (iTempInterrupt)) + { + CBTHidConnection + *connection = + static_cast ((*iBTConnContainer)[i]); + __ASSERT_ALWAYS(connection, PanicServer(EInvalidHandle)); + connection->OfferInterruptSocket(devAddr, + iTempInterrupt); + i++; + } + } + else + { + //Commented for avoiding display the same notes twice for same device + //because of two channels(Control+Interrupt) has been rejected + //InformStatusChange(devAddr, EBTDeviceAnotherExist); + } + + // The next socket to accept into + if (iTempInterrupt) + { + // Reuse this socket + iTempInterrupt->Close(); + err = KErrNone; + } + else + { + // Socket ownership has been transferred so create a new + // socket + //TRAP( err, iTempInterrupt = new (ELeave) RSocket; ) + iTempInterrupt = new RSocket; + } + + // Created a socket to accept into so accept next connection + if (err == KErrNone) + { + err = iInterruptListener->AcceptConnection( + *iTempInterrupt); + } + + // If we failed to allocate a new RSocket or failed + // in the AcceptConnectionL call it means we can no longer + // accept connections from a device. + if (err != KErrNone) + { + TRACE_INFO(_L("[BTHID]\tCBTHidServer::SocketAccepted, interrupt channel failed, shutdown listener")); + ShutdownListeners(err); + } + break; + + default: + PanicServer(EInvalidHandle); + break; + } + } + + } + +//from MTransportLayer +TUint CBTHidServer::CountryCodeL(TInt aConnID) + { + // Identify the connection object. + CBTHidConnection* conn = IdentifyConnectionL(aConnID); + // Retrieve its device details. + const CBTHidDevice& device = conn->DeviceDetails(); + + //return the country code + return device.iCountryCode; + } + +TUint CBTHidServer::VendorIdL(TInt aConnID) + { + // Identify the connection object. + CBTHidConnection* conn = IdentifyConnectionL(aConnID); + // Retrieve its device details. + const CBTHidDevice& device = conn->DeviceDetails(); + + //return the Vendor Id. + return device.iVendorID; + } + +TUint CBTHidServer::ProductIdL(TInt aConnID) + { + // Identify the connection object. + CBTHidConnection* conn = IdentifyConnectionL(aConnID); + // Retrieve its device details. + const CBTHidDevice& device = conn->DeviceDetails(); + + //return the Product Id. + return device.iProductID; + } + +void CBTHidServer::GetProtocolL(TInt aConnID, TUint16 /*aInterface*/) + { + CBTHidConnection *connection = IdentifyConnectionL(aConnID); + + // Bluetooth HID only has one interface. We don't need the interface param. + connection->GetProtocolL(); + } + +void CBTHidServer::SetProtocolL(TInt aConnID, TUint16 aValue, TUint16 /*aInterface*/) + { + CBTHidConnection *connection = IdentifyConnectionL(aConnID); + + // Bluetooth HID only has one interface. We don't need the interface param. + connection->SetProtocolL(aValue); + } + +void CBTHidServer::GetReportL(TInt aConnID, TUint8 aReportType, + TUint8 aReportID, TUint16 /*aInterface*/, TUint16 aLength) + { + CBTHidConnection *connection = IdentifyConnectionL(aConnID); + + // Bluetooth HID only has one interface. We don't need the interface param. + connection->GetReportL(aReportType, aReportID, aLength); + } + +void CBTHidServer::SetReportL(TInt aConnID, TUint8 aReportType, + TUint8 aReportID, TUint16 /*aInterface*/, const TDesC8& aReport) + { + CBTHidConnection *connection = IdentifyConnectionL(aConnID); + + // Bluetooth HID only has one interface. We don't need the interface param + connection->SetReportL(aReportType, aReportID, aReport); + } + +void CBTHidServer::DataOutL(TInt aConnID, TUint8 aReportID, + TUint16 /*aInterface*/, const TDesC8& aReport) + { + CBTHidConnection *connection = IdentifyConnectionL(aConnID); + + // Bluetooth HID only has one interface. We don't need the interface param + connection->DataOutL(aReportID, aReport); //we disregard DATA Input, DATA Feature and DATA Other + } + +void CBTHidServer::GetIdleL(TInt aConnID, TUint8 /*aReportID*/, TUint16 /*aInterface*/) + { + CBTHidConnection *connection = IdentifyConnectionL(aConnID); + + // Bluetooth HID only has one interface. We don't need the interface param. + // Bluetooth HID doesn't specify Report ID. + connection->GetIdleL(); + } + +void CBTHidServer::SetIdleL(TInt aConnID, TUint8 aDuration, + TUint8 /*aReportID*/, TUint16 /*aInterface*/) + { + CBTHidConnection *connection = IdentifyConnectionL(aConnID); + + // Bluetooth HID only has one interface. We don't need the interface param. + // Bluetooth HID doesn't specify Report ID. + connection->SetIdleL(aDuration); + } + +CBTHidConnection* CBTHidServer::IdentifyConnectionL(TInt aConnID) const + { + CBTHidConnection *connection = + static_cast (iBTConnIndex->At(aConnID)); + + if (!connection) + { + User::Leave(KErrNotFound); + } + + return connection; + } + +void CBTHidServer::LoadVirtuallyCabledDevicesL(const TDesC& aStoreName) + { + TRACE_INFO(_L("[BTHID]\tCBTHidServer::LoadVirtuallyCabledDevicesL")); + // Create the parsed name and open the store + TParse parsedName; + User::LeaveIfError(iFs.Parse(aStoreName, parsedName)); + + CFileStore* store = CDirectFileStore::OpenLC(iFs, parsedName.FullName(), + EFileRead); + + // Open the data stream inside the store + RStoreReadStream stream; + stream.OpenLC(*store, store->Root()); + + // Read the version number of the data file. + // Its not used in this version. + stream.ReadInt32L(); + + // Get the number of devices to load. + TInt count = stream.ReadInt32L(); + TInt i; + for (i = 0; i < count; i++) + { + // Create the connection object. Ok to leave. + CBTHidConnection* connection = CBTHidConnection::NewLC(iSocketServ, + *this, ENotConnected); + + stream >> connection->DeviceDetails(); + // Check if no security is needed, then override earlier Control listener with unsecure one. + // This is needed for Microsoft Keyboard fix. + CheckAndSetControlListenerSecurityL( + connection->DeviceDetails().iUseSecurity); + + // Add to the connection container object. + iBTConnContainer->AddL(connection); + CleanupStack::Pop(); // connection + + // We can't let this just leave since we have already inserted the + // connection object into the container. + TBool connectionIndexed = EFalse; + TRAPD( res, + // Now add the object to the index to get an id. + TInt id = iBTConnIndex->AddL(connection); + connection->SetConnID(id); + connectionIndexed = ETrue; + connection->ReconnectL(); + + // Inform the Generic HID of the connection, but don't + // start the driver yet. This will be done in LinkRestored. + GenericHIDConnectL(connection, EFalse); + ) + + if (res != KErrNone) + { + if (connectionIndexed) + { + // Connection object was added to the index, but reconnection + // failed. + iBTConnIndex->Remove(connection->ConnID()); + } + else + { + // Couldn't make an index entry. + // Close the connection object, causing it to be removed from + // the container. + connection->Close(); + } + } + } + + // Delete all the remaining stuff on the cleanup stack + // (store and stream) + CleanupStack::PopAndDestroy(2); + } + +void CBTHidServer::StoreVirtuallyCabledDevicesL(const TDesC& aStoreName) + { + TRACE_INFO(_L("[BTHID]\tCBTHidServer::StoreVirtuallyCabledDevicesL")); + // Parse the filestore name into a usable version. + TParse parsedName; + User::LeaveIfError(iFs.Parse(aStoreName, parsedName)); + + // Count the number of devices that we will need to store. + // Also count the accumulated disk size require for each one. + TInt numVirtuallyCabled = 0; + TInt requiredDiskSize = 0; + + TInt i; + + for (i = 0; i < iBTConnContainer->Count(); i++) + { + CBTHidConnection *connection = + static_cast ((*iBTConnContainer)[i]); + + if (connection) + { + CBTHidDevice &device = connection->DeviceDetails(); + if (device.iVirtuallyCabled) + { + numVirtuallyCabled++; + // Increase the required size. + requiredDiskSize += device.DiskSize(); + } + } + } + + // Check for required disk size + // NOTE !! Be careful when changing functionality in here + + // We have the number of bytes required to store all the device objects + // we want to store + + // First thing written will be the file version which is 4 bytes + // Then the number of virtually cabled devices which is 4 bytes + requiredDiskSize += 8; + + TRACE_INFO( (_L("[BTHID]\tCBTHidServer::StoreVirtuallyCabledDevicesL: Required disk size %d bytes"), + requiredDiskSize) ); + // Now check that the required number of bytes will not take us into + // the critical disk space area. + if (SysUtil::DiskSpaceBelowCriticalLevelL(&iFs, requiredDiskSize, EDriveC)) + { + User::Leave(KErrDiskFull); + } + + // Start the write for real. + + CFileStore* store = CDirectFileStore::ReplaceLC(iFs, + parsedName.FullName(), EFileWrite); + store->SetTypeL(KDirectFileStoreLayoutUid); + + // Create the stream + RStoreWriteStream stream; + TStreamId id = stream.CreateLC(*store); + + // Write the version number of the data file + stream.WriteInt32L(KDataFileVersionNumber); + + // Write the number of devices we are going to store. + stream.WriteInt32L(numVirtuallyCabled); + + // Externalise each device object that is virtually cabled. + for (i = 0; i < iBTConnContainer->Count(); i++) + { + CBTHidConnection *connection = + static_cast ((*iBTConnContainer)[i]); + + if (connection) + { + CBTHidDevice &device = connection->DeviceDetails(); + if (device.iVirtuallyCabled) + { + stream << device; + } + } + + } + + // Commit the changes to the stream + stream.CommitL(); + CleanupStack::PopAndDestroy(); + + // Set the stream in the store and commit the store + store->SetRootL(id); + store->CommitL(); + CleanupStack::PopAndDestroy(); + } + +void CBTHidServer::StoreVirtuallyCabledDevices(const TDesC& aStoreName) + { + TRAP_IGNORE( StoreVirtuallyCabledDevicesL(aStoreName);) + } + +void RunServerL() + { + TRACE_FUNC_ENTRY + User::RenameThread(KBTHidSrvName); + // 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) + CBTHidServer* btHidServer = CBTHidServer::NewLC(); + // Initialisation complete, now signal the client + RProcess::Rendezvous(KErrNone); + // The server is not up and running. + TRACE_INFO( ( _L( "[BTHID]\t BTHID server now up and running" ) ) ) + // The active scheduler runs during the lifetime of this thread. + CActiveScheduler::Start(); + // Cleanup the server and scheduler. + CleanupStack::PopAndDestroy(btHidServer); + CleanupStack::PopAndDestroy(scheduler); + TRACE_FUNC_EXIT + } + +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; + } + +// CBTHidNotifierHelper + +CBTHidNotifierHelper* CBTHidNotifierHelper::NewL(CBTHidServer& aHidServer, TInt aNote, const TBTDevAddr& aDeviceAddr) + { + CBTHidNotifierHelper* self = new(ELeave) CBTHidNotifierHelper(aHidServer, aNote, aDeviceAddr); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + +CBTHidNotifierHelper::~CBTHidNotifierHelper() + { + Cancel(); + iNotifier.Close(); + } + +void CBTHidNotifierHelper::Start() + { + TRACE_INFO(_L("CBTHidNotifierHelper::Start()...")); + + // Simple async handling + iNotifier.StartNotifierAndGetResponse(iStatus, + KBTGenericInfoNotifierUid, iGenericInfoNotifierType, + iNoResult); + + // assert object is not already active + __ASSERT_DEBUG(!IsActive(), CBTHidServer::PanicServer(ENotifierAlreadyActive)); + + SetActive(); + } + +void CBTHidNotifierHelper::DoCancel() + { + TRACE_INFO(_L("CBTHidNotifierHelper::DoCancel()...")); + + iNotifier.CancelNotifier(KBTGenericInfoNotifierUid); + } + +CBTHidNotifierHelper::CBTHidNotifierHelper(CBTHidServer& aHidServer, TInt aNote, const TBTDevAddr& aDeviceAddr) + : CActive(CActive::EPriorityStandard), + iHidServer(aHidServer) + { + TRACE_INFO(_L("CBTHidNotifierHelper::CBTHidNotifierHelper()...")); + + iGenericInfoNotifierType().iMessageType = (TBTGenericInfoNoteType) aNote; + iGenericInfoNotifierType().iRemoteAddr.Copy(aDeviceAddr.Des()); + + CActiveScheduler::Add(this); + } + +void CBTHidNotifierHelper::ConstructL() + { + TRACE_INFO(_L("CBTHidNotifierHelper::ConstructL()...")); + + User::LeaveIfError(iNotifier.Connect()); + } + +void CBTHidNotifierHelper::RunL() + { + TRACE_INFO(_L("CBTHidNotifierHelper::RunL()...")); + + iHidServer.NotifierRequestCompleted(); + }