/**************************************************************************** ** ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the Technology Preview License Agreement accompanying ** this package. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** ** ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "symbianengine.h" #include "qnetworksession_impl.h" #include #include #include #include #include #include // For randgen seeding #include // For randgen seeding #ifdef QT_BEARERMGMT_SYMBIAN_DEBUG #include #endif #ifdef SNAP_FUNCTIONALITY_AVAILABLE #include #include #include #include #include #include #else #include #include #include #endif #ifndef QT_NO_BEARERMANAGEMENT QT_BEGIN_NAMESPACE static const int KUserChoiceIAPId = 0; SymbianNetworkConfigurationPrivate::SymbianNetworkConfigurationPrivate() : bearer(BearerUnknown), numericId(0), connectionId(0) { } SymbianNetworkConfigurationPrivate::~SymbianNetworkConfigurationPrivate() { } QString SymbianNetworkConfigurationPrivate::bearerName() const { QMutexLocker locker(&mutex); switch (bearer) { case BearerEthernet: return QLatin1String("Ethernet"); case BearerWLAN: return QLatin1String("WLAN"); case Bearer2G: return QLatin1String("2G"); case BearerCDMA2000: return QLatin1String("CDMA2000"); case BearerWCDMA: return QLatin1String("WCDMA"); case BearerHSPA: return QLatin1String("HSPA"); case BearerBluetooth: return QLatin1String("Bluetooth"); case BearerWiMAX: return QLatin1String("WiMAX"); default: return QString(); } } SymbianEngine::SymbianEngine(QObject *parent) : QBearerEngine(parent), CActive(CActive::EPriorityHigh), iFirstUpdate(true), iInitOk(true), iUpdatePending(false) { } void SymbianEngine::initialize() { QMutexLocker locker(&mutex); CActiveScheduler::Add(this); // Seed the randomgenerator qsrand(QTime(0,0,0).secsTo(QTime::currentTime()) + QCoreApplication::applicationPid()); TRAPD(error, ipCommsDB = CCommsDatabase::NewL(EDatabaseTypeIAP)); if (error != KErrNone) { iInitOk = false; return; } TRAP_IGNORE(iConnectionMonitor.ConnectL()); #ifdef SNAP_FUNCTIONALITY_AVAILABLE TRAP_IGNORE(iConnectionMonitor.SetUintAttribute(EBearerIdAll, 0, KBearerGroupThreshold, 1)); #endif TRAP_IGNORE(iConnectionMonitor.NotifyEventL(*this)); #ifdef SNAP_FUNCTIONALITY_AVAILABLE TRAP(error, iCmManager.OpenL()); if (error != KErrNone) { iInitOk = false; return; } #endif SymbianNetworkConfigurationPrivate *cpPriv = new SymbianNetworkConfigurationPrivate; cpPriv->name = "UserChoice"; cpPriv->bearer = SymbianNetworkConfigurationPrivate::BearerUnknown; cpPriv->state = QNetworkConfiguration::Discovered; cpPriv->isValid = true; cpPriv->id = QString::number(qHash(KUserChoiceIAPId)); cpPriv->numericId = KUserChoiceIAPId; cpPriv->connectionId = 0; cpPriv->type = QNetworkConfiguration::UserChoice; cpPriv->purpose = QNetworkConfiguration::UnknownPurpose; cpPriv->roamingSupported = false; QNetworkConfigurationPrivatePointer ptr(cpPriv); userChoiceConfigurations.insert(ptr->id, ptr); updateConfigurations(); updateStatesToSnaps(); updateAvailableAccessPoints(); // On first time updates synchronously (without WLAN scans) // Start monitoring IAP and/or SNAP changes in Symbian CommsDB startCommsDatabaseNotifications(); iFirstUpdate = false; } SymbianEngine::~SymbianEngine() { Cancel(); iConnectionMonitor.CancelNotifications(); iConnectionMonitor.Close(); #ifdef SNAP_FUNCTIONALITY_AVAILABLE iCmManager.Close(); #endif delete ipAccessPointsAvailabilityScanner; // CCommsDatabase destructor uses cleanup stack. Since QNetworkConfigurationManager // is a global static, but the time we are here, E32Main() has been exited already and // the thread's default cleanup stack has been deleted. Without this line, a // 'E32USER-CBase 69' -panic will occur. CTrapCleanup* cleanup = CTrapCleanup::New(); delete ipCommsDB; delete cleanup; } void SymbianEngine::delayedConfigurationUpdate() { QMutexLocker locker(&mutex); if (iUpdatePending) { #ifdef QT_BEARERMGMT_SYMBIAN_DEBUG qDebug("QNCM delayed configuration update (ECommit or ERecover occurred)."); #endif TRAPD(error, updateConfigurationsL()); if (error == KErrNone) { updateStatesToSnaps(); } iUpdatePending = false; // Start monitoring again. if (!IsActive()) { SetActive(); // Start waiting for new notification ipCommsDB->RequestNotification(iStatus); } } } bool SymbianEngine::hasIdentifier(const QString &id) { QMutexLocker locker(&mutex); return accessPointConfigurations.contains(id) || snapConfigurations.contains(id) || userChoiceConfigurations.contains(id); } QNetworkConfigurationManager::Capabilities SymbianEngine::capabilities() const { QNetworkConfigurationManager::Capabilities capFlags; capFlags = QNetworkConfigurationManager::CanStartAndStopInterfaces | QNetworkConfigurationManager::DirectConnectionRouting | QNetworkConfigurationManager::SystemSessionSupport | QNetworkConfigurationManager::DataStatistics | QNetworkConfigurationManager::NetworkSessionRequired; #ifdef SNAP_FUNCTIONALITY_AVAILABLE capFlags |= QNetworkConfigurationManager::ApplicationLevelRoaming | QNetworkConfigurationManager::ForcedRoaming; #endif return capFlags; } QNetworkSessionPrivate *SymbianEngine::createSessionBackend() { return new QNetworkSessionPrivateImpl(this); } void SymbianEngine::requestUpdate() { QMutexLocker locker(&mutex); if (!iInitOk || iUpdateGoingOn) { return; } iUpdateGoingOn = true; stopCommsDatabaseNotifications(); updateConfigurations(); // Synchronous call updateAvailableAccessPoints(); // Asynchronous call } void SymbianEngine::updateConfigurations() { if (!iInitOk) return; TRAP_IGNORE(updateConfigurationsL()); } void SymbianEngine::updateConfigurationsL() { QList knownConfigs = accessPointConfigurations.keys(); QList knownSnapConfigs = snapConfigurations.keys(); #ifdef SNAP_FUNCTIONALITY_AVAILABLE // S60 version is >= Series60 3rd Edition Feature Pack 2 TInt error = KErrNone; // Loop through all IAPs RArray connectionMethods; // IAPs CleanupClosePushL(connectionMethods); iCmManager.ConnectionMethodL(connectionMethods); for(int i = 0; i < connectionMethods.Count(); i++) { RCmConnectionMethod connectionMethod = iCmManager.ConnectionMethodL(connectionMethods[i]); CleanupClosePushL(connectionMethod); TUint32 iapId = connectionMethod.GetIntAttributeL(CMManager::ECmIapId); QString ident = QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX+QString::number(qHash(iapId)); if (accessPointConfigurations.contains(ident)) { knownConfigs.removeOne(ident); } else { SymbianNetworkConfigurationPrivate* cpPriv = NULL; TRAP(error, cpPriv = configFromConnectionMethodL(connectionMethod)); if (error == KErrNone) { QNetworkConfigurationPrivatePointer ptr(cpPriv); accessPointConfigurations.insert(ptr->id, ptr); mutex.unlock(); // Emit configuration added. Connected slots may throw execptions // which propagate here --> must be converted to leaves (standard // std::exception would cause any TRAP trapping this function to terminate // program). QT_TRYCATCH_LEAVING(emit configurationAdded(ptr)); mutex.lock(); } } CleanupStack::PopAndDestroy(&connectionMethod); } CleanupStack::PopAndDestroy(&connectionMethods); // Loop through all SNAPs RArray destinations; CleanupClosePushL(destinations); iCmManager.AllDestinationsL(destinations); for(int i = 0; i < destinations.Count(); i++) { RCmDestination destination; // Some destinatsions require ReadDeviceData -capability (MMS/WAP) // The below function will leave in these cases. Don't. Proceed to // next destination (if any). TRAPD(error, destination = iCmManager.DestinationL(destinations[i])); if (error == KErrPermissionDenied) { continue; } else { User::LeaveIfError(error); } CleanupClosePushL(destination); QString ident = QT_BEARERMGMT_CONFIGURATION_SNAP_PREFIX + QString::number(qHash(destination.Id())); if (snapConfigurations.contains(ident)) { knownSnapConfigs.removeOne(ident); } else { SymbianNetworkConfigurationPrivate *cpPriv = new SymbianNetworkConfigurationPrivate; HBufC *pName = destination.NameLC(); QT_TRYCATCH_LEAVING(cpPriv->name = QString::fromUtf16(pName->Ptr(),pName->Length())); CleanupStack::PopAndDestroy(pName); pName = NULL; cpPriv->isValid = true; cpPriv->id = ident; cpPriv->numericId = destination.Id(); cpPriv->connectionId = 0; cpPriv->state = QNetworkConfiguration::Defined; cpPriv->type = QNetworkConfiguration::ServiceNetwork; cpPriv->purpose = QNetworkConfiguration::UnknownPurpose; cpPriv->roamingSupported = false; QNetworkConfigurationPrivatePointer ptr(cpPriv); snapConfigurations.insert(ident, ptr); mutex.unlock(); QT_TRYCATCH_LEAVING(emit configurationAdded(ptr)); mutex.lock(); } // Loop through all connection methods in this SNAP QMap connections; for (int j=0; j < destination.ConnectionMethodCount(); j++) { RCmConnectionMethod connectionMethod = destination.ConnectionMethodL(j); CleanupClosePushL(connectionMethod); TUint32 iapId = connectionMethod.GetIntAttributeL(CMManager::ECmIapId); QString iface = QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX+QString::number(qHash(iapId)); // Check that IAP can be found from accessPointConfigurations list QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(iface); if (ptr) { knownConfigs.removeOne(iface); } else { SymbianNetworkConfigurationPrivate *cpPriv = NULL; TRAP(error, cpPriv = configFromConnectionMethodL(connectionMethod)); if (error == KErrNone) { ptr = QNetworkConfigurationPrivatePointer(cpPriv); accessPointConfigurations.insert(ptr->id, ptr); mutex.unlock(); QT_TRYCATCH_LEAVING(emit configurationAdded(ptr)); mutex.lock(); } } if (ptr) { unsigned int priority; TRAPD(error, priority = destination.PriorityL(connectionMethod)); if (!error) connections.insert(priority, ptr); } CleanupStack::PopAndDestroy(&connectionMethod); } QNetworkConfigurationPrivatePointer privSNAP = snapConfigurations.value(ident); QMutexLocker snapConfigLocker(&privSNAP->mutex); if (privSNAP->serviceNetworkMembers != connections) { privSNAP->serviceNetworkMembers = connections; // Roaming is supported only if SNAP contains more than one IAP privSNAP->roamingSupported = privSNAP->serviceNetworkMembers.count() > 1; snapConfigLocker.unlock(); mutex.unlock(); QT_TRYCATCH_LEAVING(emit configurationChanged(privSNAP)); mutex.lock(); } CleanupStack::PopAndDestroy(&destination); } CleanupStack::PopAndDestroy(&destinations); #else // S60 version is < Series60 3rd Edition Feature Pack 2 CCommsDbTableView* pDbTView = ipCommsDB->OpenTableLC(TPtrC(IAP)); // Loop through all IAPs TUint32 apId = 0; TInt retVal = pDbTView->GotoFirstRecord(); while (retVal == KErrNone) { pDbTView->ReadUintL(TPtrC(COMMDB_ID), apId); QString ident = QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX+QString::number(qHash(apId)); if (accessPointConfigurations.contains(ident)) { knownConfigs.removeOne(ident); } else { SymbianNetworkConfigurationPrivate *cpPriv = new SymbianNetworkConfigurationPrivate; if (readNetworkConfigurationValuesFromCommsDb(apId, cpPriv)) { QNetworkConfigurationPrivatePointer ptr(cpPriv); accessPointConfigurations.insert(ident, ptr); mutex.unlock(); QT_TRYCATCH_LEAVING(emit configurationAdded(ptr)); mutex.lock(); } else { delete cpPriv; } } retVal = pDbTView->GotoNextRecord(); } CleanupStack::PopAndDestroy(pDbTView); #endif QT_TRYCATCH_LEAVING(updateActiveAccessPoints()); foreach (const QString &oldIface, knownConfigs) { //remove non existing IAP QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.take(oldIface); mutex.unlock(); emit configurationRemoved(ptr); QT_TRYCATCH_LEAVING(emit configurationRemoved(ptr)); mutex.lock(); // Remove non existing IAP from SNAPs foreach (const QString &iface, snapConfigurations.keys()) { QNetworkConfigurationPrivatePointer ptr2 = snapConfigurations.value(iface); // => Check if one of the IAPs of the SNAP is active QMutexLocker snapConfigLocker(&ptr2->mutex); QMutableMapIterator i(ptr2->serviceNetworkMembers); while (i.hasNext()) { i.next(); if (toSymbianConfig(i.value())->numericIdentifier() == toSymbianConfig(ptr)->numericIdentifier()) { i.remove(); break; } } } } foreach (const QString &oldIface, knownSnapConfigs) { //remove non existing SNAPs QNetworkConfigurationPrivatePointer ptr = snapConfigurations.take(oldIface); mutex.unlock(); emit configurationRemoved(ptr); QT_TRYCATCH_LEAVING(emit configurationRemoved(ptr)); mutex.lock(); } // find default configuration. stopCommsDatabaseNotifications(); TRAP_IGNORE(defaultConfig = defaultConfigurationL()); startCommsDatabaseNotifications(); } #ifdef SNAP_FUNCTIONALITY_AVAILABLE SymbianNetworkConfigurationPrivate *SymbianEngine::configFromConnectionMethodL( RCmConnectionMethod& connectionMethod) { SymbianNetworkConfigurationPrivate *cpPriv = new SymbianNetworkConfigurationPrivate; TUint32 iapId = connectionMethod.GetIntAttributeL(CMManager::ECmIapId); QString ident = QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX+QString::number(qHash(iapId)); HBufC *pName = connectionMethod.GetStringAttributeL(CMManager::ECmName); CleanupStack::PushL(pName); QT_TRYCATCH_LEAVING(cpPriv->name = QString::fromUtf16(pName->Ptr(),pName->Length())); CleanupStack::PopAndDestroy(pName); pName = NULL; TUint32 bearerId = connectionMethod.GetIntAttributeL(CMManager::ECmCommsDBBearerType); switch (bearerId) { case KCommDbBearerCSD: cpPriv->bearer = SymbianNetworkConfigurationPrivate::Bearer2G; break; case KCommDbBearerWcdma: cpPriv->bearer = SymbianNetworkConfigurationPrivate::BearerWCDMA; break; case KCommDbBearerLAN: cpPriv->bearer = SymbianNetworkConfigurationPrivate::BearerEthernet; break; case KCommDbBearerVirtual: cpPriv->bearer = SymbianNetworkConfigurationPrivate::BearerUnknown; break; case KCommDbBearerPAN: cpPriv->bearer = SymbianNetworkConfigurationPrivate::BearerUnknown; break; case KCommDbBearerWLAN: cpPriv->bearer = SymbianNetworkConfigurationPrivate::BearerWLAN; break; default: cpPriv->bearer = SymbianNetworkConfigurationPrivate::BearerUnknown; break; } TInt error = KErrNone; TUint32 bearerType = connectionMethod.GetIntAttributeL(CMManager::ECmBearerType); switch (bearerType) { case KUidPacketDataBearerType: // "Packet data" Bearer => Mapping is done using "Access point name" TRAP(error, pName = connectionMethod.GetStringAttributeL(CMManager::EPacketDataAPName)); break; case KUidWlanBearerType: // "Wireless LAN" Bearer => Mapping is done using "WLAN network name" = SSID TRAP(error, pName = connectionMethod.GetStringAttributeL(CMManager::EWlanSSID)); break; } if (!pName) { // "Data call" Bearer or "High Speed (GSM)" Bearer => Mapping is done using "Dial-up number" TRAP(error, pName = connectionMethod.GetStringAttributeL(CMManager::EDialDefaultTelNum)); } if (error == KErrNone && pName) { CleanupStack::PushL(pName); QT_TRYCATCH_LEAVING(cpPriv->mappingName = QString::fromUtf16(pName->Ptr(),pName->Length())); CleanupStack::PopAndDestroy(pName); pName = NULL; } cpPriv->state = QNetworkConfiguration::Defined; TBool isConnected = connectionMethod.GetBoolAttributeL(CMManager::ECmConnected); if (isConnected) { cpPriv->state = QNetworkConfiguration::Active; } cpPriv->isValid = true; cpPriv->id = ident; cpPriv->numericId = iapId; cpPriv->connectionId = 0; cpPriv->type = QNetworkConfiguration::InternetAccessPoint; cpPriv->purpose = QNetworkConfiguration::UnknownPurpose; cpPriv->roamingSupported = false; return cpPriv; } #else bool SymbianEngine::readNetworkConfigurationValuesFromCommsDb( TUint32 aApId, SymbianNetworkConfigurationPrivate *apNetworkConfiguration) { TRAPD(error, readNetworkConfigurationValuesFromCommsDbL(aApId,apNetworkConfiguration)); if (error != KErrNone) { return false; } return true; } void SymbianEngine::readNetworkConfigurationValuesFromCommsDbL( TUint32 aApId, SymbianNetworkConfigurationPrivate *apNetworkConfiguration) { CApDataHandler* pDataHandler = CApDataHandler::NewLC(*ipCommsDB); CApAccessPointItem* pAPItem = CApAccessPointItem::NewLC(); TBuf name; CApUtils* pApUtils = CApUtils::NewLC(*ipCommsDB); TUint32 apId = pApUtils->WapIdFromIapIdL(aApId); pDataHandler->AccessPointDataL(apId,*pAPItem); pAPItem->ReadTextL(EApIapName, name); if (name.Compare(_L("Easy WLAN")) == 0) { // "Easy WLAN" won't be accepted to the Configurations list User::Leave(KErrNotFound); } QString ident = QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX+QString::number(qHash(aApId)); QT_TRYCATCH_LEAVING(apNetworkConfiguration->name = QString::fromUtf16(name.Ptr(),name.Length())); apNetworkConfiguration->isValid = true; apNetworkConfiguration->id = ident; apNetworkConfiguration->numericId = aApId; apNetworkConfiguration->connectionId = 0; apNetworkConfiguration->state = (QNetworkConfiguration::Defined); apNetworkConfiguration->type = QNetworkConfiguration::InternetAccessPoint; apNetworkConfiguration->purpose = QNetworkConfiguration::UnknownPurpose; apNetworkConfiguration->roamingSupported = false; switch (pAPItem->BearerTypeL()) { case EApBearerTypeCSD: apNetworkConfiguration->bearer = SymbianNetworkConfigurationPrivate::Bearer2G; break; case EApBearerTypeGPRS: apNetworkConfiguration->bearer = SymbianNetworkConfigurationPrivate::Bearer2G; break; case EApBearerTypeHSCSD: apNetworkConfiguration->bearer = SymbianNetworkConfigurationPrivate::BearerHSPA; break; case EApBearerTypeCDMA: apNetworkConfiguration->bearer = SymbianNetworkConfigurationPrivate::BearerCDMA2000; break; case EApBearerTypeWLAN: apNetworkConfiguration->bearer = SymbianNetworkConfigurationPrivate::BearerWLAN; break; case EApBearerTypeLAN: apNetworkConfiguration->bearer = SymbianNetworkConfigurationPrivate::BearerEthernet; break; case EApBearerTypeLANModem: apNetworkConfiguration->bearer = SymbianNetworkConfigurationPrivate::BearerEthernet; break; default: apNetworkConfiguration->bearer = SymbianNetworkConfigurationPrivate::BearerUnknown; break; } CleanupStack::PopAndDestroy(pApUtils); CleanupStack::PopAndDestroy(pAPItem); CleanupStack::PopAndDestroy(pDataHandler); } #endif QNetworkConfigurationPrivatePointer SymbianEngine::defaultConfiguration() { QMutexLocker locker(&mutex); return defaultConfig; } QStringList SymbianEngine::accessPointConfigurationIdentifiers() { QMutexLocker locker(&mutex); return accessPointConfigurations.keys(); } QNetworkConfigurationPrivatePointer SymbianEngine::defaultConfigurationL() { QNetworkConfigurationPrivatePointer ptr; #ifdef SNAP_FUNCTIONALITY_AVAILABLE // Check Default Connection (SNAP or IAP) TCmDefConnValue defaultConnectionValue; iCmManager.ReadDefConnL(defaultConnectionValue); if (defaultConnectionValue.iType == ECmDefConnDestination) { QString iface = QT_BEARERMGMT_CONFIGURATION_SNAP_PREFIX + QString::number(qHash(defaultConnectionValue.iId)); ptr = snapConfigurations.value(iface); } else if (defaultConnectionValue.iType == ECmDefConnConnectionMethod) { QString iface = QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX + QString::number(qHash(defaultConnectionValue.iId)); ptr = accessPointConfigurations.value(iface); } #endif if (ptr) { QMutexLocker configLocker(&ptr->mutex); if (ptr->isValid) return ptr; } QString iface = QString::number(qHash(KUserChoiceIAPId)); return userChoiceConfigurations.value(iface); } void SymbianEngine::updateActiveAccessPoints() { bool online = false; QList inactiveConfigs = accessPointConfigurations.keys(); TRequestStatus status; TUint connectionCount; iConnectionMonitor.GetConnectionCount(connectionCount, status); User::WaitForRequest(status); // Go through all connections and set state of related IAPs to Active. // Status needs to be checked carefully, because ConnMon lists also e.g. // WLAN connections that are being currently tried --> we don't want to // state these as active. TUint connectionId; TUint subConnectionCount; TUint apId; TInt connectionStatus; if (status.Int() == KErrNone) { for (TUint i = 1; i <= connectionCount; i++) { iConnectionMonitor.GetConnectionInfo(i, connectionId, subConnectionCount); iConnectionMonitor.GetUintAttribute(connectionId, subConnectionCount, KIAPId, apId, status); User::WaitForRequest(status); QString ident = QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX+QString::number(qHash(apId)); QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(ident); #ifdef OCC_FUNCTIONALITY_AVAILABLE if (!ptr) { // If IAP was not found, check if the update was about EasyWLAN ptr = configurationFromEasyWlan(apId, connectionId); // Change the ident correspondingly if (ptr) { ident = QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX + QString::number(qHash(toSymbianConfig(ptr)->numericIdentifier())); } } #endif if (ptr) { iConnectionMonitor.GetIntAttribute(connectionId, subConnectionCount, KConnectionStatus, connectionStatus, status); User::WaitForRequest(status); if (connectionStatus == KLinkLayerOpen) { online = true; inactiveConfigs.removeOne(ident); ptr->mutex.lock(); toSymbianConfig(ptr)->connectionId = connectionId; ptr->mutex.unlock(); // Configuration is Active changeConfigurationStateTo(ptr, QNetworkConfiguration::Active); } } } } // Make sure that state of rest of the IAPs won't be Active foreach (const QString &iface, inactiveConfigs) { QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(iface); if (ptr) { // Configuration is either Defined or Discovered changeConfigurationStateAtMaxTo(ptr, QNetworkConfiguration::Discovered); } } if (iOnline != online) { iOnline = online; mutex.unlock(); emit this->onlineStateChanged(online); mutex.lock(); } } void SymbianEngine::updateAvailableAccessPoints() { if (!ipAccessPointsAvailabilityScanner) { ipAccessPointsAvailabilityScanner = new AccessPointsAvailabilityScanner(*this, iConnectionMonitor); } if (ipAccessPointsAvailabilityScanner) { // Scanning may take a while because WLAN scanning will be done (if device supports WLAN). ipAccessPointsAvailabilityScanner->StartScanning(); } } void SymbianEngine::accessPointScanningReady(TBool scanSuccessful, TConnMonIapInfo iapInfo) { iUpdateGoingOn = false; if (scanSuccessful) { QList unavailableConfigs = accessPointConfigurations.keys(); // Set state of returned IAPs to Discovered // if state is not already Active for(TUint i=0; imutex); if (ptr->state < QNetworkConfiguration::Active) { // Configuration is either Discovered or Active changeConfigurationStateAtMinTo(ptr, QNetworkConfiguration::Discovered); } } } // Make sure that state of rest of the IAPs won't be Active foreach (const QString &iface, unavailableConfigs) { QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(iface); if (ptr) { // Configuration is Defined changeConfigurationStateAtMaxTo(ptr, QNetworkConfiguration::Discovered); } } } updateStatesToSnaps(); if (!iFirstUpdate) { startCommsDatabaseNotifications(); mutex.unlock(); emit updateCompleted(); mutex.lock(); } } void SymbianEngine::updateStatesToSnaps() { // Go through SNAPs and set correct state to SNAPs foreach (const QString &iface, snapConfigurations.keys()) { bool discovered = false; bool active = false; QNetworkConfigurationPrivatePointer ptr = snapConfigurations.value(iface); QMutexLocker snapConfigLocker(&ptr->mutex); // => Check if one of the IAPs of the SNAP is discovered or active // => If one of IAPs is active, also SNAP is active // => If one of IAPs is discovered but none of the IAPs is active, SNAP is discovered QMapIterator i(ptr->serviceNetworkMembers); while (i.hasNext()) { i.next(); const QNetworkConfigurationPrivatePointer child = i.value(); QMutexLocker configLocker(&child->mutex); if ((child->state & QNetworkConfiguration::Active) == QNetworkConfiguration::Active) { active = true; break; } else if ((child->state & QNetworkConfiguration::Discovered) == QNetworkConfiguration::Discovered) { discovered = true; } } if (active) { changeConfigurationStateTo(ptr, QNetworkConfiguration::Active); } else if (discovered) { changeConfigurationStateTo(ptr, QNetworkConfiguration::Discovered); } else { changeConfigurationStateTo(ptr, QNetworkConfiguration::Defined); } } } #ifdef SNAP_FUNCTIONALITY_AVAILABLE void SymbianEngine::updateMobileBearerToConfigs(TConnMonBearerInfo bearerInfo) { QHash::const_iterator i = accessPointConfigurations.constBegin(); while (i != accessPointConfigurations.constEnd()) { QNetworkConfigurationPrivatePointer ptr = i.value(); QMutexLocker locker(&ptr->mutex); SymbianNetworkConfigurationPrivate *p = toSymbianConfig(ptr); if (p->bearer >= SymbianNetworkConfigurationPrivate::Bearer2G && p->bearer <= SymbianNetworkConfigurationPrivate::BearerHSPA) { switch (bearerInfo) { case EBearerInfoCSD: p->bearer = SymbianNetworkConfigurationPrivate::Bearer2G; break; case EBearerInfoWCDMA: p->bearer = SymbianNetworkConfigurationPrivate::BearerWCDMA; break; case EBearerInfoCDMA2000: p->bearer = SymbianNetworkConfigurationPrivate::BearerCDMA2000; break; case EBearerInfoGPRS: p->bearer = SymbianNetworkConfigurationPrivate::Bearer2G; break; case EBearerInfoHSCSD: p->bearer = SymbianNetworkConfigurationPrivate::Bearer2G; break; case EBearerInfoEdgeGPRS: p->bearer = SymbianNetworkConfigurationPrivate::Bearer2G; break; case EBearerInfoWcdmaCSD: p->bearer = SymbianNetworkConfigurationPrivate::BearerWCDMA; break; case EBearerInfoHSDPA: p->bearer = SymbianNetworkConfigurationPrivate::BearerHSPA; break; case EBearerInfoHSUPA: p->bearer = SymbianNetworkConfigurationPrivate::BearerHSPA; break; case EBearerInfoHSxPA: p->bearer = SymbianNetworkConfigurationPrivate::BearerHSPA; break; } } ++i; } } #endif bool SymbianEngine::changeConfigurationStateTo(QNetworkConfigurationPrivatePointer ptr, QNetworkConfiguration::StateFlags newState) { ptr->mutex.lock(); if (newState != ptr->state) { ptr->state = newState; ptr->mutex.unlock(); mutex.unlock(); emit configurationChanged(ptr); mutex.lock(); return true; } else { ptr->mutex.unlock(); } return false; } /* changeConfigurationStateAtMinTo function does not overwrite possible better * state (e.g. Discovered state does not overwrite Active state) but * makes sure that state is at minimum given state. */ bool SymbianEngine::changeConfigurationStateAtMinTo(QNetworkConfigurationPrivatePointer ptr, QNetworkConfiguration::StateFlags newState) { ptr->mutex.lock(); if ((newState | ptr->state) != ptr->state) { ptr->state = (ptr->state | newState); ptr->mutex.unlock(); mutex.unlock(); emit configurationChanged(ptr); mutex.lock(); return true; } else { ptr->mutex.unlock(); } return false; } /* changeConfigurationStateAtMaxTo function overwrites possible better * state (e.g. Discovered state overwrites Active state) and * makes sure that state is at maximum given state (e.g. Discovered state * does not overwrite Defined state). */ bool SymbianEngine::changeConfigurationStateAtMaxTo(QNetworkConfigurationPrivatePointer ptr, QNetworkConfiguration::StateFlags newState) { ptr->mutex.lock(); if ((newState & ptr->state) != ptr->state) { ptr->state = (newState & ptr->state); ptr->mutex.unlock(); mutex.unlock(); emit configurationChanged(ptr); mutex.lock(); return true; } else { ptr->mutex.unlock(); } return false; } void SymbianEngine::startCommsDatabaseNotifications() { if (!iWaitingCommsDatabaseNotifications) { iWaitingCommsDatabaseNotifications = ETrue; if (!IsActive()) { SetActive(); // Start waiting for new notification ipCommsDB->RequestNotification(iStatus); } } } void SymbianEngine::stopCommsDatabaseNotifications() { if (iWaitingCommsDatabaseNotifications) { iWaitingCommsDatabaseNotifications = EFalse; Cancel(); } } void SymbianEngine::RunL() { QMutexLocker locker(&mutex); if (iStatus != KErrCancel) { // By default, start relistening notifications. Stop only if interesting event occured. iWaitingCommsDatabaseNotifications = true; RDbNotifier::TEvent event = STATIC_CAST(RDbNotifier::TEvent, iStatus.Int()); switch (event) { case RDbNotifier::ECommit: /** A transaction has been committed. */ case RDbNotifier::ERecover: /** The database has been recovered */ #ifdef QT_BEARERMGMT_SYMBIAN_DEBUG qDebug("QNCM CommsDB event (of type RDbNotifier::TEvent) received: %d", iStatus.Int()); #endif // Mark that there is update pending. No need to ask more events, // as we know we will be updating anyway when the timer expires. if (!iUpdatePending) { iUpdatePending = true; iWaitingCommsDatabaseNotifications = false; // Update after random time, so that many processes won't // start updating simultaneously updateConfigurationsAfterRandomTime(); } break; default: // Do nothing break; } } if (iWaitingCommsDatabaseNotifications) { if (!IsActive()) { SetActive(); // Start waiting for new notification ipCommsDB->RequestNotification(iStatus); } } } void SymbianEngine::DoCancel() { QMutexLocker locker(&mutex); ipCommsDB->CancelRequestNotification(); } void SymbianEngine::EventL(const CConnMonEventBase& aEvent) { QMutexLocker locker(&mutex); switch (aEvent.EventType()) { #ifdef SNAP_FUNCTIONALITY_AVAILABLE case EConnMonBearerInfoChange: { CConnMonBearerInfoChange* realEvent; realEvent = (CConnMonBearerInfoChange*) &aEvent; TUint connectionId = realEvent->ConnectionId(); if (connectionId == EBearerIdAll) { //Network level event TConnMonBearerInfo bearerInfo = (TConnMonBearerInfo)realEvent->BearerInfo(); updateMobileBearerToConfigs(bearerInfo); } break; } #endif case EConnMonConnectionStatusChange: { CConnMonConnectionStatusChange* realEvent; realEvent = (CConnMonConnectionStatusChange*) &aEvent; TInt connectionStatus = realEvent->ConnectionStatus(); #ifdef QT_BEARERMGMT_SYMBIAN_DEBUG qDebug() << "QNCM Connection status : " << QString::number(connectionStatus) << " , connection monitor Id : " << realEvent->ConnectionId(); #endif if (connectionStatus == KConfigDaemonStartingRegistration) { TUint connectionId = realEvent->ConnectionId(); TUint subConnectionCount = 0; TUint apId; TRequestStatus status; iConnectionMonitor.GetUintAttribute(connectionId, subConnectionCount, KIAPId, apId, status); User::WaitForRequest(status); QString ident = QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX+QString::number(qHash(apId)); QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(ident); #ifdef OCC_FUNCTIONALITY_AVAILABLE if (!ptr) { // Check if status was regarding EasyWLAN ptr = configurationFromEasyWlan(apId, connectionId); } #endif if (ptr) { ptr->mutex.lock(); toSymbianConfig(ptr)->connectionId = connectionId; ptr->mutex.unlock(); QT_TRYCATCH_LEAVING( emit configurationStateChanged(toSymbianConfig(ptr)->numericIdentifier(), connectionId, QNetworkSession::Connecting) ); } } else if (connectionStatus == KLinkLayerOpen) { // Connection has been successfully opened TUint connectionId = realEvent->ConnectionId(); TUint subConnectionCount = 0; TUint apId; TRequestStatus status; iConnectionMonitor.GetUintAttribute(connectionId, subConnectionCount, KIAPId, apId, status); User::WaitForRequest(status); QString ident = QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX+QString::number(qHash(apId)); QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(ident); #ifdef OCC_FUNCTIONALITY_AVAILABLE if (!ptr) { // Check for EasyWLAN ptr = configurationFromEasyWlan(apId, connectionId); } #endif if (ptr) { ptr->mutex.lock(); toSymbianConfig(ptr)->connectionId = connectionId; ptr->mutex.unlock(); // Configuration is Active QT_TRYCATCH_LEAVING( if (changeConfigurationStateTo(ptr, QNetworkConfiguration::Active)) { updateStatesToSnaps(); } emit configurationStateChanged(toSymbianConfig(ptr)->numericIdentifier(), connectionId, QNetworkSession::Connected); if (!iOnline) { iOnline = true; emit this->onlineStateChanged(iOnline); } ); } } else if (connectionStatus == KConfigDaemonStartingDeregistration) { TUint connectionId = realEvent->ConnectionId(); QNetworkConfigurationPrivatePointer ptr = dataByConnectionId(connectionId); if (ptr) { QT_TRYCATCH_LEAVING( emit configurationStateChanged(toSymbianConfig(ptr)->numericIdentifier(), connectionId, QNetworkSession::Closing) ); } } else if (connectionStatus == KLinkLayerClosed || connectionStatus == KConnectionClosed) { // Connection has been closed. Which of the above events is reported, depends on the Symbian // platform. TUint connectionId = realEvent->ConnectionId(); QNetworkConfigurationPrivatePointer ptr = dataByConnectionId(connectionId); if (ptr) { // Configuration is either Defined or Discovered QT_TRYCATCH_LEAVING( if (changeConfigurationStateAtMaxTo(ptr, QNetworkConfiguration::Discovered)) { updateStatesToSnaps(); } emit configurationStateChanged(toSymbianConfig(ptr)->numericIdentifier(), connectionId, QNetworkSession::Disconnected); ); } bool online = false; foreach (const QString &iface, accessPointConfigurations.keys()) { QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(iface); QMutexLocker configLocker(&ptr->mutex); if (ptr->state == QNetworkConfiguration::Active) { online = true; break; } } if (iOnline != online) { iOnline = online; QT_TRYCATCH_LEAVING(emit this->onlineStateChanged(iOnline)); } } } break; case EConnMonIapAvailabilityChange: { CConnMonIapAvailabilityChange* realEvent; realEvent = (CConnMonIapAvailabilityChange*) &aEvent; TConnMonIapInfo iaps = realEvent->IapAvailability(); QList unDiscoveredConfigs = accessPointConfigurations.keys(); for ( TUint i = 0; i < iaps.Count(); i++ ) { QString ident = QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX + QString::number(qHash(iaps.iIap[i].iIapId)); QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(ident); if (ptr) { // Configuration is either Discovered or Active QT_TRYCATCH_LEAVING(changeConfigurationStateAtMinTo(ptr, QNetworkConfiguration::Discovered)); unDiscoveredConfigs.removeOne(ident); } } foreach (const QString &iface, unDiscoveredConfigs) { QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(iface); if (ptr) { // Configuration is Defined QT_TRYCATCH_LEAVING(changeConfigurationStateAtMaxTo(ptr, QNetworkConfiguration::Defined)); } } } break; case EConnMonCreateConnection: { // This event is caught to keep connection monitor IDs up-to-date. CConnMonCreateConnection* realEvent; realEvent = (CConnMonCreateConnection*) &aEvent; TUint subConnectionCount = 0; TUint apId; TUint connectionId = realEvent->ConnectionId(); TRequestStatus status; iConnectionMonitor.GetUintAttribute(connectionId, subConnectionCount, KIAPId, apId, status); User::WaitForRequest(status); QString ident = QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX+QString::number(qHash(apId)); QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(ident); #ifdef OCC_FUNCTIONALITY_AVAILABLE if (!ptr) { // If IAP was not found, check if the update was about EasyWLAN ptr = configurationFromEasyWlan(apId, connectionId); } #endif if (ptr) { QMutexLocker configLocker(&ptr->mutex); #ifdef QT_BEARERMGMT_SYMBIAN_DEBUG qDebug() << "QNCM updating connection monitor ID : from, to, whose: " << toSymbianConfig(ptr)->connectionId << connectionId << ptr->name; #endif toSymbianConfig(ptr)->connectionId = connectionId; } } break; default: // For unrecognized events break; } } #ifdef OCC_FUNCTIONALITY_AVAILABLE // Tries to derive configuration from EasyWLAN. // First checks if the interface brought up was EasyWLAN, then derives the real SSID, // and looks up configuration based on that one. QNetworkConfigurationPrivatePointer SymbianEngine::configurationFromEasyWlan(TUint32 apId, TUint connectionId) { if (apId == iCmManager.EasyWlanIdL()) { TRequestStatus status; TBuf<50> easyWlanNetworkName; iConnectionMonitor.GetStringAttribute( connectionId, 0, KNetworkName, easyWlanNetworkName, status ); User::WaitForRequest(status); if (status.Int() == KErrNone) { QString realSSID = QString::fromUtf16(easyWlanNetworkName.Ptr(), easyWlanNetworkName.Length()); // Browser through all items and check their name for match QHash >::const_iterator i = accessPointConfigurations.constBegin(); while (i != accessPointConfigurations.constEnd()) { QNetworkConfigurationPrivatePointer ptr = i.value(); QMutexLocker configLocker(&ptr->mutex); if (ptr->name == realSSID) { #ifdef QT_BEARERMGMT_SYMBIAN_DEBUG qDebug() << "QNCM EasyWlan uses real SSID: " << realSSID; #endif return ptr; } ++i; } } } return QNetworkConfigurationPrivatePointer(); } bool SymbianEngine::easyWlanTrueIapId(TUint32& trueIapId) { // Check if this is easy wlan id in the first place if (trueIapId != iCmManager.EasyWlanIdL()) return false; // Loop through all connections that connection monitor is aware // and check for IAPs based on easy WLAN TRequestStatus status; TUint connectionCount; iConnectionMonitor.GetConnectionCount(connectionCount, status); User::WaitForRequest(status); TUint connectionId; TUint subConnectionCount; TUint apId; if (status.Int() == KErrNone) { for (TUint i = 1; i <= connectionCount; i++) { iConnectionMonitor.GetConnectionInfo(i, connectionId, subConnectionCount); iConnectionMonitor.GetUintAttribute(connectionId, subConnectionCount, KIAPId, apId, status); User::WaitForRequest(status); if (apId == trueIapId) { QNetworkConfigurationPrivatePointer ptr = configurationFromEasyWlan(apId, connectionId); if (ptr) { #ifdef QT_BEARERMGMT_SYMBIAN_DEBUG qDebug() << "QNCM easyWlanTrueIapId(), found true IAP ID: " << toSymbianConfig(ptr)->numericIdentifier(); #endif trueIapId = toSymbianConfig(ptr)->numericIdentifier(); return true; } } } } return false; } #endif // Sessions may use this function to report configuration state changes, // because on some Symbian platforms (especially Symbian^3) all state changes are not // reported by the RConnectionMonitor, in particular in relation to stop() call, // whereas they _are_ reported on RConnection progress notifier used by sessions --> centralize // this data here so that other sessions may benefit from it too (not all sessions necessarily have // RConnection progress notifiers available but they relay on having e.g. disconnected information from // manager). Currently only 'Disconnected' state is of interest because it has proven to be troublesome. void SymbianEngine::configurationStateChangeReport(TUint32 accessPointId, QNetworkSession::State newState) { QMutexLocker locker(&mutex); #ifdef QT_BEARERMGMT_SYMBIAN_DEBUG qDebug() << "QNCM A session reported state change for IAP ID: " << accessPointId << " whose new state is: " << newState; #endif switch (newState) { case QNetworkSession::Disconnected: { QString ident = QT_BEARERMGMT_CONFIGURATION_IAP_PREFIX + QString::number(qHash(accessPointId)); QNetworkConfigurationPrivatePointer ptr = accessPointConfigurations.value(ident); if (ptr) { // Configuration is either Defined or Discovered if (changeConfigurationStateAtMaxTo(ptr, QNetworkConfiguration::Discovered)) { updateStatesToSnaps(); } locker.unlock(); emit configurationStateChanged(toSymbianConfig(ptr)->numericIdentifier(), toSymbianConfig(ptr)->connectionIdentifier(), QNetworkSession::Disconnected); locker.relock(); } } break; default: break; } } // Waits for 2..6 seconds. void SymbianEngine::updateConfigurationsAfterRandomTime() { int iTimeToWait = qMax(1000, (qAbs(qrand()) % 68) * 100); #ifdef QT_BEARERMGMT_SYMBIAN_DEBUG qDebug("QNCM waiting random time: %d ms", iTimeToWait); #endif QTimer::singleShot(iTimeToWait, this, SLOT(delayedConfigurationUpdate())); } QNetworkConfigurationPrivatePointer SymbianEngine::dataByConnectionId(TUint aConnectionId) { QNetworkConfiguration item; QHash::const_iterator i = accessPointConfigurations.constBegin(); while (i != accessPointConfigurations.constEnd()) { QNetworkConfigurationPrivatePointer ptr = i.value(); if (toSymbianConfig(ptr)->connectionIdentifier() == aConnectionId) return ptr; ++i; } return QNetworkConfigurationPrivatePointer(); } AccessPointsAvailabilityScanner::AccessPointsAvailabilityScanner(SymbianEngine& owner, RConnectionMonitor& connectionMonitor) : CActive(CActive::EPriorityHigh), iOwner(owner), iConnectionMonitor(connectionMonitor) { CActiveScheduler::Add(this); } AccessPointsAvailabilityScanner::~AccessPointsAvailabilityScanner() { Cancel(); } void AccessPointsAvailabilityScanner::DoCancel() { iConnectionMonitor.CancelAsyncRequest(EConnMonGetPckgAttribute); } void AccessPointsAvailabilityScanner::StartScanning() { if (iOwner.iFirstUpdate) { // On first update (the mgr is being instantiated) update only those bearers who // don't need time-consuming scans (WLAN). // Note: EBearerIdWCDMA covers also GPRS bearer iConnectionMonitor.GetPckgAttribute(EBearerIdWCDMA, 0, KIapAvailability, iIapBuf, iStatus); User::WaitForRequest(iStatus); if (iStatus.Int() == KErrNone) { iOwner.accessPointScanningReady(true,iIapBuf()); } } else { iConnectionMonitor.GetPckgAttribute(EBearerIdAll, 0, KIapAvailability, iIapBuf, iStatus); if (!IsActive()) { SetActive(); } } } void AccessPointsAvailabilityScanner::RunL() { QMutexLocker locker(&iOwner.mutex); if (iStatus.Int() != KErrNone) { iIapBuf().iCount = 0; QT_TRYCATCH_LEAVING(iOwner.accessPointScanningReady(false,iIapBuf())); } else { QT_TRYCATCH_LEAVING(iOwner.accessPointScanningReady(true,iIapBuf())); } } QT_END_NAMESPACE #endif // QT_NO_BEARERMANAGEMENT