atext/plugins/limitedpdpplugin/src/limitedpdpplugin.cpp
branchRCL_3
changeset 22 786b94c6f0a4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/atext/plugins/limitedpdpplugin/src/limitedpdpplugin.cpp	Thu Aug 19 11:01:00 2010 +0300
@@ -0,0 +1,420 @@
+/*
+ * Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * All rights reserved.
+ * This component and the accompanying materials are made available
+ * under the terms of "Eclipse Public License v1.0"
+ * which accompanies this distribution, and is available
+ * at the URL "http://www.eclipse.org/legal/epl-v10.html".
+ *
+ * Initial Contributors:
+ * Nokia Corporation - initial contribution.
+ *
+ * Contributors:
+ *
+ * Description:  Main handler for incoming requests
+ *
+ */
+
+#include "limitedpdpplugin.h"
+#include <centralrepository.h>
+#include "limitedpdpplugincopiedkeys.h"
+#include "debug.h"
+
+// Internal constants
+const TInt KDialupOverrideEnabled = 1;
+const TInt KCloseTimeoutInterval = 15000000; // 15 secs
+const TInt KCleanUpWaitTimeout = 300000; // 300 ms
+#ifdef _DEBUG 
+const TInt KUsecToMSecDivider = 1000;
+#endif
+
+// ---------------------------------------------------------------------------
+// Two-phased constructor.
+// ---------------------------------------------------------------------------
+//
+CLimitedPdpPlugin* CLimitedPdpPlugin::NewL()
+    {
+    CLimitedPdpPlugin* self = new ( ELeave ) CLimitedPdpPlugin();
+    CleanupStack::PushL( self );
+    self->ConstructL();
+    CleanupStack::Pop( self );
+    return self;
+    }
+
+// ---------------------------------------------------------------------------
+// Destructor.
+// ---------------------------------------------------------------------------
+//
+CLimitedPdpPlugin::~CLimitedPdpPlugin()
+    {
+    TRACE_FUNC_ENTRY
+
+    // Check validity of handles so that Cancel functions can be called
+    if ( iProperty.Handle() != NULL )
+        {
+        // Cancel just in case in spite of the fact that with current
+        // architecture cancelling of the operation is not possible
+        iProperty.Cancel();
+        iProperty.Close();
+        }
+    if ( iCancelTimer.Handle() != NULL )
+        {
+        iCancelTimer.Cancel();
+        iCancelTimer.Close();
+        }
+
+    TRACE_FUNC_EXIT
+    }
+
+// ---------------------------------------------------------------------------
+// CLimitedPdpPlugin::CLimitedPdpPlugin
+// ---------------------------------------------------------------------------
+//
+CLimitedPdpPlugin::CLimitedPdpPlugin() :
+    CATExtPluginBase()
+    {
+    // Nothing to do here, resources are initialized in ConstructL
+    }
+
+// ---------------------------------------------------------------------------
+// CLimitedPdpPlugin::ConstructL
+// ---------------------------------------------------------------------------
+//
+void CLimitedPdpPlugin::ConstructL()
+    {
+    TRACE_FUNC_ENTRY
+
+    // No need to use cleanup stack here, since these will be closed in
+    // destructor anyway.
+    User::LeaveIfError( iCancelTimer.CreateLocal() );
+    User::LeaveIfError( iProperty.Attach( KCopyOfConnectionMonitorPS,
+                                          KCopyOfDialUpConnection ) );
+    iFeatureSupported = EFalse;
+    iFeatureSupported = CheckFeatureEnablement();
+
+    TRACE_FUNC_EXIT
+    }
+
+// ---------------------------------------------------------------------------
+// Reports connection identifier name to the extension plugin.
+// ---------------------------------------------------------------------------
+//
+void CLimitedPdpPlugin::ReportConnectionName( const TDesC8& /*aName*/)
+    {
+    }
+
+// ---------------------------------------------------------------------------
+// Reports the support status of an AT command. This is a synchronous API.
+// ---------------------------------------------------------------------------
+//
+TBool CLimitedPdpPlugin::IsCommandSupported( const TDesC8& /*aCmd*/)
+    {
+    TRACE_FUNC_ENTRY
+    // Set the return value according to feature support
+    TRACE_INFO(( _L("Returning feature support value %d"),
+                    static_cast<TInt>(iFeatureSupported)));
+    TRACE_FUNC_EXIT
+    return iFeatureSupported;
+    }
+
+// ---------------------------------------------------------------------------
+// Handles an AT command. Cancelling of the pending request is done by
+// HandleCommandCancel(). The implementation in the extension plugin should
+// be asynchronous, but in this case we must use blocking behavior. This has
+// a couple of drawbacks. First, the ATEXT thread is blocked and possible,
+// which is against the requirements, and secondly and more importantly, the
+// cancelling of this operation is not supported.
+// ---------------------------------------------------------------------------
+//
+void CLimitedPdpPlugin::HandleCommand( const TDesC8& /*aCmd*/,
+                                       RBuf8& /*aReply*/,
+                                       TBool /*aReplyNeeded*/)
+    {
+    TRACE_FUNC_ENTRY
+
+    // Double check that we are actually supporting the feature
+    if ( iFeatureSupported )
+        {
+        // Request ConnMon to close existing packet data connections
+        TInt retTemp = CloseExistingConnections();
+        if ( retTemp == KErrNone )
+            {
+            retTemp = BlockThreadUntilConnectionsClosed();
+            if ( retTemp == KErrNone )
+                {
+                // Internal connections closed. We need to wait for a while
+                // to make sure that lower layer resources are properly 
+                // cleaned in order to avoid conflicts in resource
+                // allocation. Again thread needs to be blocked, therefore
+                // User::After.
+                // NOTE: This is definitely a bad workaround and unreliable
+                // approach, but there aren't too many options...
+                TRACE_INFO(( _L("Block for %d ms for resource cleanup"),
+                                ( KCleanUpWaitTimeout / KUsecToMSecDivider )));
+                User::After( KCleanUpWaitTimeout );
+                TRACE_INFO(( _L("Cleanup wait completed, exit")));
+                }
+            }
+        }
+
+    TRACE_FUNC_EXIT
+    }
+
+// ---------------------------------------------------------------------------
+// Cancels a pending HandleCommand request.
+// ---------------------------------------------------------------------------
+//
+void CLimitedPdpPlugin::HandleCommandCancel()
+    {
+    TRACE_FUNC_ENTRY
+    TRACE_FUNC_EXIT
+    }
+
+// ---------------------------------------------------------------------------
+// Next reply part's length.
+// The value must be equal or less than KDefaultCmdBufLength.
+// When the reply from this method is zero, ATEXT stops calling
+// GetNextPartOfReply().
+// ---------------------------------------------------------------------------
+//
+TInt CLimitedPdpPlugin::NextReplyPartLength()
+    {
+    TRACE_FUNC_ENTRY
+    TRACE_FUNC_EXIT
+    return KErrNotSupported;
+    }
+
+// ---------------------------------------------------------------------------
+// Gets the next part of reply initially set by HandleCommandComplete().
+// Length of aNextReply must be equal or less than KDefaultCmdBufLength.
+// ---------------------------------------------------------------------------
+//
+TInt CLimitedPdpPlugin::GetNextPartOfReply( RBuf8& /*aNextReply*/)
+    {
+    TRACE_FUNC_ENTRY
+    TRACE_FUNC_EXIT
+    return KErrNotSupported;
+    }
+
+// ---------------------------------------------------------------------------
+// Receives unsolicited results. Cancelling of the pending request is done by
+// by ReceiveUnsolicitedResultCancel(). The implementation in the extension
+// plugin should be asynchronous.
+// ---------------------------------------------------------------------------
+//
+void CLimitedPdpPlugin::ReceiveUnsolicitedResult()
+    {
+    TRACE_FUNC_ENTRY
+    TRACE_FUNC_EXIT
+    }
+
+// ---------------------------------------------------------------------------
+// Cancels a pending ReceiveUnsolicitedResult request.
+// ---------------------------------------------------------------------------
+//
+void CLimitedPdpPlugin::ReceiveUnsolicitedResultCancel()
+    {
+    TRACE_FUNC_ENTRY
+    TRACE_FUNC_EXIT
+    }
+
+// ---------------------------------------------------------------------------
+// Reports NVRAM status change to the plugins.
+// ---------------------------------------------------------------------------
+//
+void CLimitedPdpPlugin::ReportNvramStatusChange( const TDesC8& /*aNvram*/)
+    {
+    TRACE_FUNC_ENTRY
+    TRACE_FUNC_EXIT
+    }
+
+// ---------------------------------------------------------------------------
+// Reports about external handle command error condition.
+// This is for cases when for example DUN decided the reply contained an
+// error condition but the plugin is still handling the command internally.
+// Example: "AT+TEST;+TEST2" was given in command line; "AT+TEST" returns
+// non-EReplyTypeError condition and "AT+TEST2" returns EReplyTypeError.
+// As the plugin(s) returning the non-EReplyTypeError may still have some
+// ongoing operation then these plugins are notified about the external
+// EReplyTypeError in command line processing. It is to be noted that
+// HandleCommandCancel() is not sufficient to stop the processing as the
+// command handling has already finished.
+// ---------------------------------------------------------------------------
+//
+void CLimitedPdpPlugin::ReportExternalHandleCommandError()
+    {
+    TRACE_FUNC_ENTRY
+    TRACE_FUNC_EXIT
+    }
+
+// ---------------------------------------------------------------------------
+// Reads CenRep key to check whether requested functionality is active.
+// ---------------------------------------------------------------------------
+//
+TBool CLimitedPdpPlugin::CheckFeatureEnablement()
+    {
+    TRACE_FUNC_ENTRY
+    TBool enabled( EFalse );
+    CRepository* cmRepository = NULL;
+    TRAP_IGNORE( cmRepository = CRepository::NewL ( KCopyOfCRUidCmManager ) );
+    if ( cmRepository )
+        {
+        TInt overrideValue = KErrNotFound;
+        TInt retTemp = cmRepository->Get( KCopyOfDialUpOverride,
+                                          overrideValue );
+
+        if ( ( retTemp == KErrNone ) && ( overrideValue
+                == KDialupOverrideEnabled ) )
+            {
+            enabled = ETrue;
+            TRACE_INFO(( _L("Dialup override feature enabled")));
+            }
+        }
+    delete cmRepository;
+    TRACE_FUNC_EXIT
+    return enabled;
+    }
+
+// ---------------------------------------------------------------------------
+// Ask ConnMon to close all existing packet data connections
+// ---------------------------------------------------------------------------
+//
+TInt CLimitedPdpPlugin::CloseExistingConnections()
+    {
+    TRACE_FUNC_ENTRY
+
+    TInt dialupState( EConnMonReady );
+    TInt retVal( KErrNone );
+
+    retVal = iProperty.Get( dialupState );
+    if ( retVal == KErrNone )
+        {
+        TRACE_INFO(( _L("Current dialup connection PS key value %d"),
+                        dialupState ));
+        if ( dialupState != EConnMonDialUpInit )
+            {
+            TRACE_INFO(( _L("Setting dialup connection PS key to value %d"),
+                            EConnMonDialUpInit ));
+            retVal = iProperty.Set( EConnMonDialUpInit );
+            }
+        else
+            {
+            // Error situation, we should not end up to this function if
+            // the connection closing is already in init state.
+            retVal = KErrNotReady;
+            }
+        }
+    TRACE_INFO(( _L("Closing existing connections done with status %d"),
+                    retVal ));
+    TRACE_FUNC_EXIT
+    return retVal;
+    }
+
+// ---------------------------------------------------------------------------
+// Synchronously block thread until ConnMon indicates that connections are
+// closed or operation timeouts.
+// ---------------------------------------------------------------------------
+//
+TInt CLimitedPdpPlugin::BlockThreadUntilConnectionsClosed()
+    {
+    TRACE_FUNC_ENTRY
+
+    TInt dialupState( EConnMonDialUpInit );
+    TInt retVal( KErrNone );
+
+    // Read the dialup value just to check if ConnMon has been able to update
+    // it already to reflect correct state.
+    retVal = iProperty.Get( dialupState );
+
+    if ( retVal == KErrNone )
+        {
+        TRACE_INFO(( _L("Dialup connection PS key value before wait: %d"),
+                        dialupState ));
+        if ( dialupState == EConnMonDialUpInit )
+            {
+            // Block thread until value changes
+            TRequestStatus propertyStatus;
+            TRequestStatus timeoutStatus;
+            // Set operation to timeout if closing internal contexts fails.
+            // If timeout expires before PS key is updated, this plugin
+            // will pass the ATD*99# command to modem. Most probably
+            // that will fail due to lack of resources. However, if
+            // internal connections are not closed within this timeframe
+            // there will be problems anyway. So this is only to hasten
+            // end user feedback on error situation. 
+            iCancelTimer.After( timeoutStatus, KCloseTimeoutInterval );
+
+            // Loop for property subscription just in case the ConnMon does
+            // not set correct value in first attempt. The loop exiting is
+            // done separately below, but this condition is better than using
+            // while ( ETrue )
+            while ( dialupState != EConnMonReady )
+                {
+                iProperty.Subscribe( propertyStatus );
+
+                TRACE_INFO(( _L("Blocking thread to wait connection closing") ));
+                User::WaitForRequest( propertyStatus, timeoutStatus );
+
+                // Wait completed, check which condition was valid
+                if ( propertyStatus != KRequestPending )
+                    {
+                    if ( propertyStatus == KErrNone )
+                        {
+                        // ConnMon changed the value, check if it is valid
+                        TInt retVal = iProperty.Get( dialupState );
+                        if ( retVal == KErrNone )
+                            {
+                            if ( dialupState == EConnMonReady )
+                                {
+                                TRACE_INFO(( _L("Existing connections closed successfully") ));
+                                }
+                            else
+                                {
+                                // Otherwise retry
+                                TRACE_INFO(( _L("Wrong internal connection state (%d), retry"),
+                                                dialupState ));
+                                continue;
+                                }
+                            }
+                        }
+                    else
+                        {
+                        retVal = propertyStatus.Int();
+                        }
+                    // We should exit the loop, either due success of error
+                    // Cancel and wait for timeout request to complete
+                    TRACE_INFO((_L("Existing connections closed (status: %d), cancel timer"),
+                                    retVal ));
+                    iCancelTimer.Cancel();
+                    // Wait... If the completion of Cancel() is not waited
+                    // here, CActiveScheduler will panic due to stray signal
+                    // (E32USER-CBase panic code 46)
+                    User::WaitForRequest( timeoutStatus );
+                    TRACE_INFO(( _L("Timeout cancelled (timeoutStatus: %d), exit wait"),
+                                    timeoutStatus.Int() ));
+                    break;
+                    }
+                else if ( timeoutStatus != KRequestPending )
+                    {
+                    // Timeout or error, exit
+                    TRACE_INFO(( _L("Wait for existing connections timeouted (status: %d)"),
+                                    timeoutStatus.Int() ));
+                    // Cancel PS subscription and wait for its completion
+                    iProperty.Cancel();
+                    User::WaitForRequest( propertyStatus );
+                    retVal = iProperty.Set( EConnMonDialUpClosed );
+                    TRACE_INFO(( _L("Subscription cancelled (status: %d) and state reset, exit"),
+                                    propertyStatus.Int() ));
+                    retVal = KErrTimedOut;
+                    break;
+                    }
+                }
+            }
+        }
+    // All done, exit
+    TRACE_INFO(( _L("Exiting from wait with status %d"), retVal ));
+
+    TRACE_FUNC_EXIT
+    return retVal;
+    }
+