src/plugins/bearer/symbian/qnetworksession_impl.cpp
changeset 37 758a864f9613
parent 33 3e2da88830cd
--- a/src/plugins/bearer/symbian/qnetworksession_impl.cpp	Fri Sep 17 08:34:18 2010 +0300
+++ b/src/plugins/bearer/symbian/qnetworksession_impl.cpp	Mon Oct 04 01:19:32 2010 +0300
@@ -48,6 +48,14 @@
 #include <stdapis/sys/socket.h>
 #include <stdapis/net/if.h>
 
+#ifdef SNAP_FUNCTIONALITY_AVAILABLE
+#include <cmmanager.h>
+#endif
+
+#if defined(OCC_FUNCTIONALITY_AVAILABLE) && defined(SNAP_FUNCTIONALITY_AVAILABLE)
+#include <extendedconnpref.h>
+#endif
+
 #ifndef QT_NO_BEARERMANAGEMENT
 
 QT_BEGIN_NAMESPACE
@@ -102,19 +110,20 @@
     // Cancel possible RConnection::Start()
     Cancel();
     iSocketServ.Close();
-    
+
     // Close global 'Open C' RConnection
     // Clears also possible unsetdefaultif() flags.
     setdefaultif(0);
-    
+
     iConnectionMonitor.Close();
     iOpenCLibrary.Close();
 #ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
-    qDebug() << "QNS this : " << QString::number((uint)this) << " - destroyed (and setdefaultif(0))";
+    qDebug() << "QNS this : " << QString::number((uint)this)
+             << " - destroyed (and setdefaultif(0))";
 #endif
 }
 
-void QNetworkSessionPrivateImpl::configurationStateChanged(TUint32 accessPointId, TUint32 connMonId, QNetworkSession::State newState)
+void QNetworkSessionPrivateImpl::configurationStateChanged(quint32 accessPointId, quint32 connMonId, QNetworkSession::State newState)
 {
     if (iHandleStateNotificationsFromManager) {
 #ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
@@ -146,62 +155,67 @@
     }
 }
 
+void QNetworkSessionPrivateImpl::configurationAdded(QNetworkConfigurationPrivatePointer config)
+{
+    Q_UNUSED(config);
+    // If session is based on service network, some other app may create new access points
+    // to the SNAP --> synchronize session's state with that of interface's.
+    if (!publicConfig.isValid() || publicConfig.type() != QNetworkConfiguration::ServiceNetwork)
+        return;
+
+#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
+        qDebug() << "QNS this : " << QString::number((uint)this) << " - "
+                 << "configurationAdded IAP: "
+                 << toSymbianConfig(privateConfiguration(config))->numericIdentifier();
+#endif
+
+        syncStateWithInterface();
+}
+
+// Function sets the state of the session to match the state
+// of the underlying interface (the configuration this session is based on)
 void QNetworkSessionPrivateImpl::syncStateWithInterface()
 {
     if (!publicConfig.isValid())
         return;
 
-    if (iFirstSync && publicConfig.isValid()) {
-        QObject::connect(engine, SIGNAL(configurationStateChanged(TUint32, TUint32, QNetworkSession::State)),
-                         this, SLOT(configurationStateChanged(TUint32, TUint32, QNetworkSession::State)));
+    if (iFirstSync) {
+        QObject::connect(engine,
+                         SIGNAL(configurationStateChanged(quint32,quint32,QNetworkSession::State)),
+                         this,
+                         SLOT(configurationStateChanged(quint32,quint32,QNetworkSession::State)));
         // Listen to configuration removals, so that in case the configuration
         // this session is based on is removed, session knows to enter Invalid -state.
         QObject::connect(engine, SIGNAL(configurationRemoved(QNetworkConfigurationPrivatePointer)),
                          this, SLOT(configurationRemoved(QNetworkConfigurationPrivatePointer)));
+        // Connect to configuration additions, so that in case a configuration is added
+        // in a SNAP this session is based on, the session knows to synch its state with its
+        // interface.
+        QObject::connect(engine, SIGNAL(configurationAdded(QNetworkConfigurationPrivatePointer)),
+                         this, SLOT(configurationAdded(QNetworkConfigurationPrivatePointer)));
     }
     // Start listening IAP state changes from QNetworkConfigurationManagerPrivate
     iHandleStateNotificationsFromManager = true;    
 
-    // Check open connections to see if there is already
-    // an open connection to selected IAP or SNAP
-    TUint count;
-    TRequestStatus status;
-    iConnectionMonitor.GetConnectionCount(count, status);
-    User::WaitForRequest(status);
-    if (status.Int() != KErrNone) {
-        return;
-    }
-
-    TUint numSubConnections;
-    TUint connectionId;
-    for (TUint i = 1; i <= count; i++) {
-        TInt ret = iConnectionMonitor.GetConnectionInfo(i, connectionId, numSubConnections);
-        if (ret == KErrNone) {
-            TUint apId;
-            iConnectionMonitor.GetUintAttribute(connectionId, 0, KIAPId, apId, status);
-            User::WaitForRequest(status);
-            if (status.Int() == KErrNone) {
-                TInt connectionStatus;
-                iConnectionMonitor.GetIntAttribute(connectionId, 0, KConnectionStatus, connectionStatus, status);
-                User::WaitForRequest(status);
-                if (connectionStatus == KLinkLayerOpen) {
-                    if (state != QNetworkSession::Closing) {
-                        if (newState(QNetworkSession::Connected, apId)) {
-                            return;
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    if (state != QNetworkSession::Connected) {
-        if ((publicConfig.state() & QNetworkConfiguration::Discovered) ==
-            QNetworkConfiguration::Discovered) {
-            newState(QNetworkSession::Disconnected);
-        } else {
-            newState(QNetworkSession::NotAvailable);
-        }
+    // Check what is the state of the configuration this session is based on
+    // and set the session in appropriate state.
+#ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
+    qDebug() << "QNS this : " << QString::number((uint)this) << " - "
+             << "syncStateWithInterface() state of publicConfig is: " << publicConfig.state();
+#endif
+    switch (publicConfig.state()) {
+    case QNetworkConfiguration::Active:
+        newState(QNetworkSession::Connected);
+        break;
+    case QNetworkConfiguration::Discovered:
+        newState(QNetworkSession::Disconnected);
+        break;
+    case QNetworkConfiguration::Defined:
+        newState(QNetworkSession::NotAvailable);
+        break;
+    case QNetworkConfiguration::Undefined:
+    default:
+        newState(QNetworkSession::Invalid);
     }
 }
 
@@ -253,7 +267,8 @@
              << "currentInterface() requested, state: " << state
              << "publicConfig validity: " << publicConfig.isValid();
     if (activeInterface.isValid())
-        qDebug() << "interface is: " << activeInterface.humanReadableName();
+        qDebug() << "QNS this : " << QString::number((uint)this) << " - "
+                 << "interface is: " << activeInterface.humanReadableName();
 #endif
 
     if (!publicConfig.isValid() || state != QNetworkSession::Connected) {
@@ -382,7 +397,7 @@
             SymbianNetworkConfigurationPrivate *symbianConfig =
                 toSymbianConfig(privateConfiguration(publicConfig));
 
-#ifdef OCC_FUNCTIONALITY_AVAILABLE
+#if defined(OCC_FUNCTIONALITY_AVAILABLE) && defined(SNAP_FUNCTIONALITY_AVAILABLE)
             // With One Click Connectivity (Symbian^3 onwards) it is possible
             // to connect silently, without any popups.
             TConnPrefList pref;
@@ -412,7 +427,7 @@
         SymbianNetworkConfigurationPrivate *symbianConfig =
             toSymbianConfig(privateConfiguration(publicConfig));
 
-#ifdef OCC_FUNCTIONALITY_AVAILABLE
+#if defined(OCC_FUNCTIONALITY_AVAILABLE) && defined(SNAP_FUNCTIONALITY_AVAILABLE)
         // On Symbian^3 if service network is not reachable, it triggers a UI (aka EasyWLAN) where
         // user can create new IAPs. To detect this, we need to store the number of IAPs
         // there was before connection was started.
@@ -519,7 +534,7 @@
     
     Cancel(); // closes iConnection
     iSocketServ.Close();
-    
+
     // Close global 'Open C' RConnection. If OpenC supports,
     // close the defaultif for good to avoid difficult timing
     // and bouncing issues of network going immediately back up
@@ -906,6 +921,14 @@
     if (iapId == 0) {
         _LIT(KSetting, "IAP\\Id");
         iConnection.GetIntSetting(KSetting, iapId);
+#ifdef SNAP_FUNCTIONALITY_AVAILABLE
+        // Check if this is an Easy WLAN configuration. On Symbian^3 RConnection may report
+        // the used configuration as 'EasyWLAN' IAP ID if someone has just opened the configuration
+        // from WLAN Scan dialog, _and_ that connection is still up. We need to find the
+        // real matching configuration. Function alters the Easy WLAN ID to real IAP ID (only if
+        // easy WLAN):
+        easyWlanTrueIapId(iapId);
+#endif
     }
 
 #ifdef SNAP_FUNCTIONALITY_AVAILABLE
@@ -944,12 +967,18 @@
                 }
             }
         } else {
-#ifdef OCC_FUNCTIONALITY_AVAILABLE
-            // On Symbian^3 (only, not earlier or Symbian^4) if the SNAP was not reachable, it triggers
-            // user choice type of activity (EasyWLAN). As a result, a new IAP may be created, and
-            // hence if was not found yet. Therefore update configurations and see if there is something new.
+#ifdef SNAP_FUNCTIONALITY_AVAILABLE
+            // On Symbian^3 (only, not earlier or Symbian^4) if the SNAP was not reachable, it
+            // triggers user choice type of activity (EasyWLAN). As a result, a new IAP may be
+            // created, and hence if was not found yet. Therefore update configurations and see if
+            // there is something new.
+
             // 1. Update knowledge from the databases.
-            engine->requestUpdate();
+            if (thread() != engine->thread())
+                QMetaObject::invokeMethod(engine, "requestUpdate", Qt::BlockingQueuedConnection);
+            else
+                engine->requestUpdate();
+
             // 2. Check if new configuration was created during connection creation
             QList<QString> knownConfigs = engine->accessPointConfigurationIdentifiers();
 #ifdef QT_BEARERMGMT_SYMBIAN_DEBUG
@@ -982,7 +1011,7 @@
             qDebug() << "QNS this : " << QString::number((uint)this) << " - "
                     << "configuration was not found, returning invalid.";
 #endif
-#endif // OCC_FUNCTIONALITY_AVAILABLE
+#endif
             // Given IAP Id was not found from known IAPs array
             return QNetworkConfiguration();
         }
@@ -1002,7 +1031,12 @@
             } else {
                 // Check if new (WLAN) IAP was created in IAP/SNAP dialog
                 // 1. Sync internal configurations array to commsdb first
-                engine->updateConfigurations();
+                if (thread() != engine->thread()) {
+                    QMetaObject::invokeMethod(engine, "requestUpdate",
+                                              Qt::BlockingQueuedConnection);
+                } else {
+                    engine->requestUpdate();
+                }
                 // 2. Check if new configuration was created during connection creation
                 QStringList knownConfigs = engine->accessPointConfigurationIdentifiers();
                 if (knownConfigs.count() > iKnownConfigsBeforeConnectionStart.count()) {
@@ -1044,6 +1078,11 @@
             TInt error = KErrNone;
             QNetworkConfiguration newActiveConfig = activeConfiguration();
             if (!newActiveConfig.isValid()) {
+                // RConnection startup was successfull but no configuration
+                // was found. That indicates that user has chosen to create a
+                // new WLAN configuration (from scan results), but that new
+                // configuration does not have access to Internet (Internet
+                // Connectivity Test, ICT, failed).
                 error = KErrGeneral;
             } else {
                 // Use name of the IAP to open global 'Open C' RConnection
@@ -1053,16 +1092,24 @@
                 strcpy(ifr.ifr_name, nameAsByteArray.constData());
                 error = setdefaultif(&ifr);
             }
-            
             if (error != KErrNone) {
                 isOpen = false;
                 isOpening = false;
                 iError = QNetworkSession::UnknownSessionError;
                 QT_TRYCATCH_LEAVING(emit QNetworkSessionPrivate::error(iError));
-                Cancel();
                 if (ipConnectionNotifier) {
                     ipConnectionNotifier->StopNotifications();
                 }
+                if (!newActiveConfig.isValid()) {
+                    // No valid configuration, bail out.
+                    // Status updates from QNCM won't be received correctly
+                    // because there is no configuration to associate them with so transit here.
+                    iConnection.Close();
+                    newState(QNetworkSession::Closing);
+                    newState(QNetworkSession::Disconnected);
+                } else {
+                    Cancel();
+                }
                 QT_TRYCATCH_LEAVING(syncStateWithInterface());
                 return;
             }
@@ -1117,7 +1164,9 @@
             isOpening = false;
             activeConfig = QNetworkConfiguration();
             serviceConfig = QNetworkConfiguration();
-            if (publicConfig.state() == QNetworkConfiguration::Undefined ||
+            if (statusCode == KErrCancel) {
+                iError = QNetworkSession::SessionAbortedError;
+            } else if (publicConfig.state() == QNetworkConfiguration::Undefined ||
                 publicConfig.state() == QNetworkConfiguration::Defined) {
                 iError = QNetworkSession::InvalidConfigurationError;
             } else {
@@ -1300,7 +1349,7 @@
                     }
                 }
             }
-#ifdef OCC_FUNCTIONALITY_AVAILABLE
+#ifdef SNAP_FUNCTIONALITY_AVAILABLE
             // If the retVal is not true here, it means that the status update may apply to an IAP outside of
             // SNAP (session is based on SNAP but follows IAP outside of it), which may occur on Symbian^3 EasyWlan.
             if (retVal == false && activeConfig.isValid() &&
@@ -1430,6 +1479,64 @@
         }
 }
 
+#if defined(SNAP_FUNCTIONALITY_AVAILABLE)
+bool QNetworkSessionPrivateImpl::easyWlanTrueIapId(TUint32 &trueIapId) const
+{
+    RCmManager iCmManager;
+    TRAPD(err, iCmManager.OpenL());
+    if (err != KErrNone)
+        return false;
+
+    // Check if this is easy wlan id in the first place
+    if (trueIapId != iCmManager.EasyWlanIdL()) {
+        iCmManager.Close();
+        return false;
+    }
+
+    iCmManager.Close();
+
+    // 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) {
+                TBuf<50>easyWlanNetworkName;
+                iConnectionMonitor.GetStringAttribute(connectionId, 0, KNetworkName,
+                                                      easyWlanNetworkName, status);
+                User::WaitForRequest(status);
+                if (status.Int() != KErrNone)
+                    continue;
+
+                const QString ssid = QString::fromUtf16(easyWlanNetworkName.Ptr(),
+                                                            easyWlanNetworkName.Length());
+
+                QNetworkConfigurationPrivatePointer ptr = engine->configurationFromSsid(ssid);
+                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
+
 ConnectionProgressNotifier::ConnectionProgressNotifier(QNetworkSessionPrivateImpl& owner, RConnection& connection)
     : CActive(CActive::EPriorityUserInput), iOwner(owner), iConnection(connection)
 {