localconnectivityservice/modematplugin/src/atcopscmd.cpp
changeset 1 388a17646e40
child 5 11d83199e2d9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/localconnectivityservice/modematplugin/src/atcopscmd.cpp	Tue Feb 02 00:45:58 2010 +0200
@@ -0,0 +1,1109 @@
+/*
+* Copyright (c) 2009 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:  Handles the commands "AT+COPS?", "AT+COPS=?" and "AT+COPS=..."
+*
+*/
+
+
+#include <MmTsy_names.h>
+#include "atcopscmd.h"
+#include "cmdpluginobserver.h"
+#include "debug.h"
+
+_LIT8( KCOPSTestCmd, "AT+COPS=?");
+_LIT8( KCOPSReadCmd, "AT+COPS?");
+_LIT8( KCOPSSetCmd,  "AT+COPS=");
+
+_LIT8(KSupportedModesStr, ",(0,1,3,4)");
+_LIT8(KSupportedFormatsStr, ",(0,1,2)"); 
+
+// The parameters are in predefined indexes in an incoming AT command. 
+const TInt KModeParameterIndex     			= 0;  
+const TInt KFormatParameterIndex   			= 1;  
+const TInt KOperatorParameterIndex 			= 2;  
+const TInt KAccessTechnologyParameterIndex  = 3;  
+
+const TInt KMinimumParameterCountWhenModePresent       = 1;  
+const TInt KMinimumParameterCountWhenFormatPresent     = 2;  
+const TInt KMinimumParameterCountWhenOperatorPresent   = 3;  
+const TInt KMinimumParameterCountWhenAccTechPresent    = 4;  
+
+// These parameter lengths are derived from 3GPP TS 27.007 V8.4.1
+const TInt KShortOperatorNameFormatLength 			= 10; 
+const TInt KLongOperatorNameFormatLength 			= 20; 
+const TInt KNumericOperatorNameFormatLength 		= 5; 
+const TInt KMaxNetworkTestResponseAdditionalSize 	= 17; // The maximun length of parts of fixed length. 
+const TInt KMaxNetworkReadResponseAdditionalSize 	= 28 ; // The maximun length of parts fixed length.
+
+// ---------------------------------------------------------------------------
+// Two-phased constructor.
+// ---------------------------------------------------------------------------
+//
+CATCOPSCmd* CATCOPSCmd::NewL( MCmdPluginObserver* aCallback )
+    {
+    CATCOPSCmd* self = new (ELeave) CATCOPSCmd( aCallback );
+    CleanupStack::PushL( self );
+    self->ConstructL();
+    CleanupStack::Pop( self );
+    return self;
+    }
+
+// ---------------------------------------------------------------------------
+// Destructor.
+// ---------------------------------------------------------------------------
+//
+CATCOPSCmd::~CATCOPSCmd()
+    {
+    iParamArray.ResetAndDestroy(); 
+    iParamArray.Close(); 
+    iPacketService.Close(); 
+    iCustomApi.Close(); 
+    iPhone.Close(); 
+    iServer.Close();
+    delete iDetectedNetworks;
+    delete iRetrieveDetectedNetworks; 
+    }
+
+// ---------------------------------------------------------------------------
+// CATCOPSCmd::CATCOPSCmd
+// ---------------------------------------------------------------------------
+//
+CATCOPSCmd::CATCOPSCmd( MCmdPluginObserver* aCallback ) :
+    CActive(EPriorityStandard),
+    iCallback( aCallback ), 
+	iFormat(RMmCustomAPI::EOperatorNameMccMnc), 
+    iRegistrationMode(EModeAutomatic), 
+    iAccTech(EAccTechNotSet), 
+    iCurrentOperation(EIdle)
+    {
+    iCmdHandlerType = ECmdHandlerTypeUndefined;
+    }
+
+// ---------------------------------------------------------------------------
+// CATCOPSCmd::ConstructL
+// ---------------------------------------------------------------------------
+//
+void CATCOPSCmd::ConstructL()
+    {
+    if ( !iCallback )
+        {
+        User::Leave( KErrGeneral );
+        }
+    CActiveScheduler::Add(this);
+    LEAVE_IF_ERROR(iServer.Connect());
+    LEAVE_IF_ERROR(iServer.LoadPhoneModule(KMmTsyModuleName));
+    LEAVE_IF_ERROR(iPhone.Open(iServer, KMmTsyPhoneName));
+    LEAVE_IF_ERROR(iCustomApi.Open(iPhone));
+    LEAVE_IF_ERROR(iPacketService.Open(iPhone));
+    iRetrieveDetectedNetworks = CRetrieveMobilePhoneDetectedNetworks::NewL(iPhone); 
+    }
+
+// ---------------------------------------------------------------------------
+// Reports the support status of an AT command. This is a synchronous API.
+// ---------------------------------------------------------------------------
+//
+TBool CATCOPSCmd::IsCommandSupported( const TDesC8& aCmd )
+    {
+    TRACE_FUNC_ENTRY
+    TInt retTemp = KErrNone;
+
+		// First test if "test" command, because the pattern is similar with the "set" command, 
+		// this is just one extra question mark longer than that. 
+    retTemp = aCmd.Compare( KCOPSTestCmd );
+    if ( retTemp == 0 )
+        {
+        iCmdHandlerType = ECmdHandlerTypeTest;
+        TRACE_FUNC_EXIT
+        return ETrue;
+        }
+
+    retTemp = aCmd.Compare( KCOPSReadCmd );
+    if ( retTemp == 0 )
+        {
+        iCmdHandlerType = ECmdHandlerTypeRead;
+        TRACE_FUNC_EXIT
+        return ETrue;
+        }
+
+	// Test if the beginning matches the test command pattern. We're skipping parameters 
+	// here on purpose, because "set" handler will create an error reply later if 
+	// parameters are not valid. 
+    retTemp = aCmd.Left(KCOPSSetCmd().Length()).Compare(KCOPSSetCmd);
+    if ( retTemp == 0 )
+        {
+        iCmdHandlerType = ECmdHandlerTypeSet;
+        TRACE_FUNC_EXIT
+        return ETrue;
+        }
+
+    iCmdHandlerType = ECmdHandlerTypeUndefined;
+    TRACE_FUNC_EXIT
+    return EFalse;
+    }
+
+// ---------------------------------------------------------------------------
+// Handles an AT command. Cancelling of the pending request is done by
+// HandleCommandCancel(). The implementation in the extension plugin should
+// be asynchronous.
+// ---------------------------------------------------------------------------
+//
+void CATCOPSCmd::HandleCommand( const TDesC8& aCmd,
+                                   RBuf8& aReply,
+                                   TBool aReplyNeeded )
+    {
+    TRACE_FUNC_ENTRY
+
+    if ( !aReplyNeeded )
+        {
+        TRACE_FUNC_EXIT
+        return;
+        }
+
+    if(iCurrentOperation != EIdle)  
+        {
+        // only one call at time allowed. If another one is passed in, 
+        // then cancel the previous and reply with an error.   
+        HandleCommandCancel(); 
+        CreateReply(EFalse); 
+        }
+    
+    if ( iCmdHandlerType == ECmdHandlerTypeUndefined )
+        {
+		CreateReply(EFalse); 
+		}
+
+    if ( iCmdHandlerType == ECmdHandlerTypeTest )
+        {
+        // Collect network data and complete in RunL 
+        iRetrieveDetectedNetworks->StartV2(iStatus); 
+        iCurrentOperation = EListAvailableNetworkOperators; 
+        SetActive(); 
+        TRACE_FUNC_EXIT
+        return;
+        }
+
+/*
+Read command returns the current mode, the currently selected operator 
+and the current Access Technology. If no operator is selected, <format>, 
+<oper> and < AcT>  are omitted.
+*/
+    if ( iCmdHandlerType == ECmdHandlerTypeRead )
+        {
+        // Collect data in two steps. First read operator name. Continue in RunL() 
+        RMobilePhone::TMobilePhoneNetworkSelectionV1 selection; 
+        RMobilePhone::TMobilePhoneNetworkSelectionV1Pckg nwSelectionSetting(selection); 
+        iPhone.GetNetworkSelectionSetting(nwSelectionSetting);
+        switch(selection.iMethod)
+            {
+            case RMobilePhone::ENetworkSelectionAutomatic: 
+                iRegistrationMode = EModeAutomatic;
+                break; 
+            case RMobilePhone::ENetworkSelectionManual: 
+                iRegistrationMode = EModeManual;
+                break; 
+            default: 
+                // Cannot get a known selection mode! 
+                TRACE_INFO(_L("CATCOPSCmd::HandleCommand() -- Cannot get a known selection mode!"));
+                CreateReply(EFalse); 
+                TRACE_FUNC_EXIT
+                return; 
+            }
+        RMobilePhone::TMobilePhoneNetworkInfoV2Pckg nwInfo(iNetworkInfo); 
+        iPhone.GetCurrentNetwork(iStatus, nwInfo); 
+        iCurrentOperation = EGetNetworkInfoOperatorName; 
+		SetActive(); 
+        TRACE_INFO((_L("CATCOPSCmd::HandleCommand() starting operation (%d)"), iCurrentOperation));
+        TRACE_FUNC_EXIT
+        return;
+        }
+
+    // Getting this far means ECmdHandlerTypeSet. There must be parameter(s), too.   
+    TRAPD(err, ExtractParametersL(aCmd)); 
+
+    // Check that we got some parameters, at least the "mode". If not, return an error: 
+	if(iParamArray.Count() < KMinimumParameterCountWhenModePresent) 
+        {
+        // Return error response, there were no parameters! 
+        TRACE_INFO(_L("CATCOPSCmd::HandleCommand() -- no parameters!"));
+        CreateReply(EFalse); 
+        TRACE_FUNC_EXIT
+        return; 
+        }
+
+	// At least the mode parameter is present at this point. Inspect it and check other parameters. 
+	TNetworkRegistrationMode mode; 
+	err = GetModeAndCheckParameterCount(iParamArray[KModeParameterIndex]->Des(), mode); 
+	if(err != KErrNone) 
+		{
+        // Return error response, invalid mode or other parameters! 
+		TRACE_INFO(_L("CATCOPSCmd::HandleCommand() -- invalid mode or other parameters!"));
+        CreateReply(EFalse); 
+	    TRACE_FUNC_EXIT
+        return; 
+		}
+
+    // At this point the iRegistrationMode is stored and the parameters are valid. 
+	iRegistrationMode = mode; 
+	TRACE_INFO(( _L("CATCOPSCmd::HandleCommand() mode stored (%d)"), iRegistrationMode));
+		
+	if(iParamArray.Count() > 1) 
+		{
+		// If also format is present, extract it and store for later reference. 
+        RMmCustomAPI::TOperatorNameType format;
+        err = GetFormatFromParameter(iParamArray[KFormatParameterIndex]->Des(), format); 
+		if(err != KErrNone) 
+		    {
+		    // Return an error, invalid format. 
+			// Previously set format is still in use. 
+	 		TRACE_INFO(_L("CATCOPSCmd::HandleCommand() -- invalid format!"));
+            CreateReply(EFalse); 
+	 		TRACE_FUNC_EXIT
+			return; 
+			}
+		// Format parameter is OK, keep it.
+		iFormat = format;  
+		TRACE_INFO(( _L("CATCOPSCmd::HandleCommand() format stored (%d)"), iFormat));
+		}
+
+	// We're done with the required parameters, it's time to start processing the command. 
+	// So do a self complete and continue in RunL(): 
+	iReply = &aReply;  // Store the reply for later reference in RunL. 
+	iCurrentOperation = EInspectModeAndProcessCommand; 
+	TRequestStatus *status = &iStatus;  
+	User::RequestComplete(status, KErrNone); 
+	SetActive(); 
+	TRACE_INFO((_L("CATCOPSCmd::HandleCommand() starting operation (%d)"), iCurrentOperation));
+    TRACE_FUNC_EXIT
+    }
+
+// ---------------------------------------------------------------------------
+// Parses the aCmd parameter and stores results in iParamArray.
+// ---------------------------------------------------------------------------
+//
+void CATCOPSCmd::ExtractParametersL(const TDesC8& aCmd) 
+	{
+    TRACE_FUNC_ENTRY
+
+    TRACE_INFO(( _L8("CATCOPSCmd::ExtractParameters() extracting (%S)"), &aCmd));
+
+    TPtrC8 parameters = aCmd.Right(aCmd.Length() - KCOPSSetCmd().Length()); 
+
+    iParamArray.ResetAndDestroy(); 
+
+    // Parse the parameters into the parameter array: 
+    TInt separatorPos;  
+    while((separatorPos = parameters.Locate(',')) != KErrNotFound)
+        {
+        TRACE_INFO(( _L("CATCOPSCmd::ExtractParameters() separator position (%d)"), separatorPos));
+        TPtrC8 param = parameters.Left(separatorPos);
+        parameters.Set(parameters.Right(parameters.Length() - (separatorPos + 1))); // Remove the extracted part + separator 
+        HBufC8 *heapParam = param.AllocL();    
+        CleanupStack::PushL( heapParam );
+        // Strip the quotation marks from the parameter: 
+        TPtr8 ptr = heapParam->Des(); 
+        RemoveQuotationMarks(ptr); 
+        TRACE_INFO(( _L8("CATCOPSCmd::ExtractParameters() appending (%S)"), &ptr));
+        iParamArray.Append(heapParam); 
+        CleanupStack::Pop( heapParam );
+        }
+
+    // Finally append the last piece of parameters: 
+    HBufC8 *param = parameters.AllocL(); 
+    CleanupStack::PushL( param );
+    TPtr8 ptr = param->Des(); 
+    RemoveQuotationMarks(ptr); 
+    TRACE_INFO(( _L8("CATCOPSCmd::ExtractParameters() appending (%S)"), &ptr));
+    iParamArray.Append(param); 
+    CleanupStack::Pop( param );
+
+    TRACE_FUNC_EXIT
+	}
+
+// ---------------------------------------------------------------------------
+// Strips all quotation parms from the string passed in. 
+// ---------------------------------------------------------------------------
+//
+void CATCOPSCmd::RemoveQuotationMarks(TPtr8& aParameter) 
+    {
+    TRACE_FUNC_ENTRY
+    // Strip the quotation marks from the parameter: 
+    TInt quotePos;  
+    while((quotePos = aParameter.Locate('"')) != KErrNotFound)
+    {
+        aParameter.Delete(quotePos,1); 
+    }
+    TRACE_FUNC_EXIT
+    }
+
+
+// ---------------------------------------------------------------------------
+// Returns the selected mode in aMode and checks the parameter count. 
+// ---------------------------------------------------------------------------
+//
+TInt CATCOPSCmd::GetModeAndCheckParameterCount(const TDesC8& aParameter, TNetworkRegistrationMode &aMode) 
+	{
+  TRACE_FUNC_ENTRY
+	TLex8 lex;
+	lex.Assign(aParameter);
+	TInt mode(0); 
+
+	TInt err = lex.Val(mode); 
+	TRACE_INFO(( _L("CATCOPSCmd::GetModeAndCheckParameterCount() mode (%d)"), mode));
+
+	if( err != KErrNone )
+		{
+        TRACE_INFO(_L("CATCOPSCmd::GetModeAndCheckParameterCount() TLex error!)"));
+        TRACE_FUNC_EXIT
+        return KErrArgument; 
+		}			
+
+  if(mode < EModeAutomatic || mode > EModeManualAutomatic || mode == EModeDeregister)
+      {
+      // Not a valid mode. 
+      TRACE_FUNC_EXIT
+      return KErrArgument; 
+      }
+
+  if( (mode == EModeManual || mode == EModeManualAutomatic)  &&  iParamArray.Count() < KMinimumParameterCountWhenOperatorPresent )
+      {
+      // Valid modes but not enough parameters. At least format and operator needed.  
+      TRACE_INFO(( _L("CATCOPSCmd::GetModeAndCheckParameterCount() not enough parameters (%d)"), iParamArray.Count()));
+      TRACE_FUNC_EXIT
+      return KErrArgument; 
+      }
+  if( mode == EModeSetFormatParameter && iParamArray.Count() < KMinimumParameterCountWhenFormatPresent )
+      {
+      // Valid mode, but not enough parameters. Format is needed.  
+      TRACE_INFO(_L("CATCOPSCmd::GetModeAndCheckParameterCount() no format parameter)"));
+      TRACE_FUNC_EXIT
+      return KErrArgument; 
+      }
+
+  // Valid mode and enough parameters. 
+  aMode = static_cast<TNetworkRegistrationMode>(mode);
+
+	TRACE_FUNC_EXIT
+	return KErrNone; 
+	}
+
+// ---------------------------------------------------------------------------
+// Converts an AT command parameter to numeric format value and checks it is valid. 
+// ---------------------------------------------------------------------------
+//
+TInt CATCOPSCmd::GetFormatFromParameter(const TDesC8& aParameter, RMmCustomAPI::TOperatorNameType &aFormat) 
+	{
+    TRACE_FUNC_ENTRY
+    TLex8 lex;
+    lex.Assign(aParameter);
+    TInt format(0); 
+    TInt err = lex.Val(format); 
+
+    if(err != KErrNone)
+        {
+        TRACE_FUNC_EXIT
+        return KErrArgument; 
+        }
+    switch(format)
+        {
+        case EFormatLong: // long by 3GPP TS 27.007 V8.4.1  
+            TRACE_INFO(_L("Format is long by 3GPP TS 27.007 V8.4.1"));
+            aFormat = RMmCustomAPI::EOperatorNameNitzFull; 
+            break; 
+        case EFormatShort: // short by 3GPP TS 27.007 V8.4.1 
+            TRACE_INFO(_L("Format is short by 3GPP TS 27.007 V8.4.1"));
+            aFormat = RMmCustomAPI::EOperatorNameNitzShort; 
+            break; 
+        case EFormatNumeric: // numeric by 3GPP TS 27.007 V8.4.1 
+            TRACE_INFO(_L("Format is numeric by 3GPP TS 27.007 V8.4.1"));
+            aFormat = RMmCustomAPI::EOperatorNameMccMnc; 
+            // Operator is numeric, conver it into S60 style. 
+            break; 
+        default: 
+            TRACE_FUNC_EXIT
+            return KErrArgument;
+        }
+
+    TRACE_FUNC_EXIT
+    return KErrNone; 
+    }
+
+// ---------------------------------------------------------------------------
+// Converts an AT command parameter to numeric access technology value and checks it is valid. 
+// ---------------------------------------------------------------------------
+//
+TInt CATCOPSCmd::GetAccTechFromParameter(const TDesC8& aParameter, TAccessTechnology& aAccTech) 
+    {
+    TRACE_FUNC_ENTRY
+    TLex8 lex;
+    lex.Assign(aParameter);
+    TInt accTech(0); 
+    TInt err = lex.Val(accTech); 
+
+    if(err != KErrNone)
+        {
+        TRACE_FUNC_EXIT
+        return KErrArgument; 
+        }
+
+    if(accTech != EGSM && accTech != EUDMA) // The only allowed access technologies. 
+        {
+        TRACE_FUNC_EXIT
+        return KErrArgument; 
+        }
+ 
+    aAccTech = static_cast<TAccessTechnology>(accTech);
+
+    TRACE_FUNC_EXIT
+    return KErrNone; 
+    }
+
+// ---------------------------------------------------------------------------
+// Converts an AT command parameter to ETel compatible operator values 
+// ---------------------------------------------------------------------------
+//
+TInt CATCOPSCmd::ConvertOperatorToMccMnc(const CMobilePhoneNetworkListV2 *aDetectedNetworks, 
+                                        const RMmCustomAPI::TOperatorNameType aFormat, 
+                                        const TBuf<KMaxOperatorNameLength>& aOperatorParameter, 
+                                        RMobilePhone::TMobilePhoneNetworkCountryCode& aMcc, 
+                                        RMobilePhone::TMobilePhoneNetworkIdentity& aMnc) 
+    {
+    TRACE_FUNC_ENTRY
+
+    if(aFormat == RMmCustomAPI::EOperatorNameMccMnc) 
+        {
+        // Check first that there are at least five characters passed in. 
+       	TChar nextChar; 
+        if(aOperatorParameter.Length() < 5)
+        	{
+			return KErrArgument; 
+        	}
+        for(int i = 0; i < 5; ++i)
+        	{
+            nextChar = aOperatorParameter[i]; 
+            if(!nextChar.IsDigit()) 
+                {
+                return KErrArgument; 
+                }
+      		}
+        // Operator is in three digit country code + two digit network code format.  
+	    // Must be converted to ETel style. The possible extra will be simply discarded. 
+     	TRACE_INFO(_L("CATCOPSCmd::ConvertOperatorToMccMnc() operator is all digits, convert it into ETel data types."));
+	    aMcc.Copy(aOperatorParameter.Left(3)); 
+	    aMnc.Copy(aOperatorParameter.Right(2)); 
+	    }
+    else  // The short or long text string formats. 
+        {
+        // Find the requested operator from the operator array.  
+        // If array is empty, return an error. 
+        if(!aDetectedNetworks)
+            {
+            TRACE_INFO(_L("CATCOPSCmd::ConvertOperatorToMccMnc() No detected networks!"));
+            TRACE_FUNC_EXIT
+            return KErrNotFound; 
+            }
+
+        RMobilePhone::TMobilePhoneNetworkInfoV2 nwInfo; 
+        for(TInt i=0; i < iDetectedNetworks->Enumerate(); ++i)
+            {
+            TRAPD(err, nwInfo = iDetectedNetworks->GetEntryL(i))  
+            if(err != KErrNone)
+                {
+                return KErrNotFound; 
+                }
+
+            if(aFormat == RMmCustomAPI::EOperatorNameNitzShort)
+                {
+                TRACE_INFO(_L("CATCOPSCmd::ConvertOperatorToMccMnc() Operator is in short format, comparing."));
+                if(nwInfo.iShortName.Compare(aOperatorParameter) == 0)
+                    {
+                    TRACE_INFO(_L("Match found."));
+                    aMcc = nwInfo.iCountryCode; 
+                    aMnc = nwInfo.iNetworkId; 
+                    TRACE_FUNC_EXIT
+                    return KErrNone; 
+                    } 
+                }
+            else if(aFormat == RMmCustomAPI::EOperatorNameNitzFull)
+                {
+                TRACE_INFO(_L("CATCOPSCmd::ConvertOperatorToMccMnc() Operator is in long format, comparing."));
+                if(nwInfo.iLongName.Compare(aOperatorParameter) == 0)
+                    {
+                    TRACE_INFO(_L("Match found."));
+                    aMcc = nwInfo.iCountryCode; 
+                    aMnc = nwInfo.iNetworkId; 
+                    TRACE_FUNC_EXIT
+                    return KErrNone; 
+                    } 
+                }
+            else        
+                {
+                TRACE_INFO(_L("CATCOPSCmd::ConvertOperatorToMccMnc() Unknown operator format!"));
+                TRACE_FUNC_EXIT
+                return KErrArgument; 
+                }
+            }
+        TRACE_INFO(_L("CATCOPSCmd::ConvertOperatorToMccMnc() Operator was not found in list!"));
+        TRACE_FUNC_EXIT
+        return KErrNotFound; 
+        }
+
+    TRACE_FUNC_EXIT
+    return KErrNone; 
+    }
+
+
+// ---------------------------------------------------------------------------
+// Initiates an automatic network registration.  
+// ---------------------------------------------------------------------------
+//
+void CATCOPSCmd::AutomaticNetworkRegistration() 
+    {
+    TRACE_FUNC_ENTRY
+    RMobilePhone::TMobilePhoneNetworkManualSelection nwInfo; 
+	iCurrentOperation = EAutomaticallyRegisterToNetwork; 
+	nwInfo.iCountry = KNullDesC;
+	nwInfo.iNetwork = KNullDesC;
+	iPhone.SelectNetwork(iStatus, EFalse, nwInfo); 
+	SetActive();  // Response will be sent in RunL 
+	TRACE_INFO((_L("CATCOPSCmd::HandleCommand() starting operation (%d)"), iCurrentOperation));
+	TRACE_FUNC_EXIT
+	}
+
+// ---------------------------------------------------------------------------
+// Initiates a manual network registration.  
+// ---------------------------------------------------------------------------
+//
+void CATCOPSCmd::ManualNetworkRegistration(const RMobilePhone::TMobilePhoneNetworkCountryCode& aMcc, 
+                                           const RMobilePhone::TMobilePhoneNetworkIdentity& aMnc) 
+    {
+	TRACE_FUNC_ENTRY
+	RMobilePhone::TMobilePhoneNetworkManualSelection nwInfo; 
+	iCurrentOperation = EManuallyRegisterToNetwork; 
+	nwInfo.iCountry.Append(aMcc);
+	nwInfo.iNetwork.Append(aMnc);
+	iPhone.SelectNetwork(iStatus, ETrue, nwInfo); 
+	SetActive(); 	// Response will be sent in RunL 
+	TRACE_INFO((_L("CATCOPSCmd::HandleCommand() starting operation (%d)"), iCurrentOperation));
+	TRACE_FUNC_EXIT
+    }
+
+// ---------------------------------------------------------------------------
+// Initiates a manual network registration and access technology selection.  
+// ---------------------------------------------------------------------------
+//
+void CATCOPSCmd::ManualNetworkRegistration(const RMobilePhone::TMobilePhoneNetworkCountryCode& aMcc, 
+                                           const RMobilePhone::TMobilePhoneNetworkIdentity& aMnc, 
+                                           const TAccessTechnology aAccTech) 
+    {
+    TRACE_FUNC_ENTRY
+	// Store access technology for later reference: 
+    iAccTech = aAccTech; 
+	// Call another overload to start the first phase of the operation: 
+	ManualNetworkRegistration(aMcc, aMnc); 
+	// Set the state again so the RunL knows to launch the next phase: 
+	iCurrentOperation = EManuallyRegisterToNetworkAndChooseAccTech; 
+  	TRACE_INFO((_L("CATCOPSCmd::HandleCommand() starting operation (%d)"), iCurrentOperation));
+	TRACE_FUNC_EXIT
+    }
+
+// ---------------------------------------------------------------------------
+// CATCOPSCmd::RunL 
+// ---------------------------------------------------------------------------
+//
+void CATCOPSCmd::RunL()
+    {
+    TRACE_FUNC_ENTRY
+	TInt err = KErrNone; 
+	if(iStatus != KErrNone)   
+	    {
+        HandleError(); 
+	    }
+    // Proceed to next step or return a response if all is done.
+    switch(iCurrentOperation)
+        {
+        case EListAvailableNetworkOperators: 
+            TRACE_INFO((_L("CATCOPSCmd::HandleCommand() completing operation (%d)"), iCurrentOperation));
+            if(iDetectedNetworks)
+                {
+                delete iDetectedNetworks;
+                iDetectedNetworks = NULL; 
+                }
+            iDetectedNetworks = iRetrieveDetectedNetworks->RetrieveListV2L(); 
+            // Then create a response. 
+            TRAP(err, ConstructNetworkListResponseL()); 
+            if(err != KErrNone)
+                {
+                // An error here means that no response has been sent. Reply with an error. 
+                CreateReply(EFalse); 
+                }
+            break; 
+        
+        case EInspectModeAndProcessCommand: 
+            // Check the mode and act accordingly 
+            TRACE_INFO((_L("CATCOPSCmd::HandleCommand() completing operation (%d)"), iCurrentOperation));
+            err = InspectModeAndProcessCommand(); 
+            if(err != KErrNone)
+                {
+                CreateReply(EFalse); 
+                }
+            break; 
+
+        case EGetNetworkInfoOperatorName: 
+            if(ConstructNetworkInfoResponse() != KErrNone)
+                {
+                // An error means that no response has been sent. Reply with an error. 
+                CreateReply(EFalse); 
+                }
+            break; 
+
+        case EManuallyRegisterToNetworkAndChooseAccTech: 
+            TRACE_INFO((_L("CATCOPSCmd::HandleCommand() completing operation (%d)"), iCurrentOperation));
+            switch(iAccTech)
+                {
+                case EGSM: 
+                    iCustomApi.SetSystemNetworkMode(iStatus, RMmCustomAPI::KCapsNetworkModeGsm);
+                    iCurrentOperation = ESetSystemNetworkBand; 
+                    SetActive(); 
+                    break; 
+                case EUDMA: 
+                    iCustomApi.SetSystemNetworkMode(iStatus, RMmCustomAPI::KCapsNetworkModeUmts);
+                    iCurrentOperation = ESetSystemNetworkBand; 
+                    SetActive(); 
+                    break; 
+                default:
+                    // No automatic registering requested, so send back an error response. 
+                    TRACE_INFO( _L("CATCOPSCmd::RunL() incorrect acc.tech., reply an error."));
+                    CreateReply(EFalse); 
+                }
+            TRACE_INFO((_L("CATCOPSCmd::HandleCommand() starting operation (%d)"), iCurrentOperation));
+            break; 
+
+        case EManuallyRegisterToNetwork: 
+        case EAutomaticallyRegisterToNetwork: 
+        case ESetSystemNetworkBand: 
+            TRACE_INFO((_L("CATCOPSCmd::HandleCommand() completing operation (%d)"), iCurrentOperation));
+            // Last step completed successfully, so create OK response. 
+            CreateReply(ETrue); 
+            break; 
+
+        default: 
+            TRACE_INFO(( _L("CATCOPSCmd::RunL() default operation (%d)!"), iCurrentOperation));
+            break; 
+        }
+    TRACE_FUNC_EXIT
+    }
+
+// ---------------------------------------------------------------------------
+// Handles an error in async call. 
+// ---------------------------------------------------------------------------
+//
+void CATCOPSCmd::HandleError()
+    {
+    TRACE_FUNC_ENTRY
+    TRACE_INFO(( _L("CATCOPSCmd::RunL() failure (%d) in operation (%d)!"), iStatus.Int(), iCurrentOperation));
+
+    // In case of failure check the operation. In some cases failures are OK.
+    switch(iCurrentOperation)
+        {
+        case EManuallyRegisterToNetwork: 
+            if(iRegistrationMode == EModeManualAutomatic)
+                {
+                // Manual registration failed, try automatic next. 
+                TRACE_INFO( _L("CATCOPSCmd::RunL() registration mode manual automatic, try automatic."));
+                AutomaticNetworkRegistration(); 
+                }
+            else 
+                {
+                // No automatic registering requested, so send back an error response. 
+                TRACE_INFO( _L("CATCOPSCmd::RunL() reply an error."));
+                CreateReply(EFalse); 
+                }
+            break; 
+        case ESetSystemNetworkBand: 
+        case EManuallyRegisterToNetworkAndChooseAccTech: 
+            // Cannot set the access technology, so set it back to EAccTechNotSet. 
+            // This prevents replying to queries with outdated or incorrect acc tech information. 
+            TRACE_INFO( _L("CATCOPSCmd::RunL() couldn't set system network band, so reset access tech."));
+            iAccTech = EAccTechNotSet; 
+            // Fall through to default, because these require an error response. 
+
+        default: 
+            // In all other cases send back an error response. 
+            TRACE_INFO( _L("CATCOPSCmd::RunL() reply an error."));
+            CreateReply(EFalse); 
+            break; 
+        }
+    TRACE_FUNC_EXIT
+    }
+
+
+// ---------------------------------------------------------------------------
+// Cancels a pending HandleCommand request.
+// ---------------------------------------------------------------------------
+//
+void CATCOPSCmd::HandleCommandCancel()
+    {
+    TRACE_FUNC_ENTRY
+    Cancel(); 
+    TRACE_FUNC_EXIT
+    }
+
+// ---------------------------------------------------------------------------
+// CATCOPSCmd::DoCancel
+// ---------------------------------------------------------------------------
+//
+void CATCOPSCmd::DoCancel()
+    {
+    TRACE_FUNC_ENTRY
+    switch(iCurrentOperation)
+        {
+        case EAutomaticallyRegisterToNetwork:
+        case EManuallyRegisterToNetwork:
+        case EManuallyRegisterToNetworkAndChooseAccTech:
+            iPhone.CancelAsyncRequest(EMobilePhoneSelectNetworkCancel);  
+            break; 
+        case EGetNetworkInfoOperatorName:
+            iPhone.CancelAsyncRequest(EMobilePhoneGetCurrentNetworkCancel);  
+            break; 
+        case ESetSystemNetworkBand:
+            iCustomApi.CancelAsyncRequest(ECustomSetSystemNetworkModeIPC); 
+            break; 
+        case EListAvailableNetworkOperators:
+            iRetrieveDetectedNetworks->Cancel();  
+            break; 
+        default: 
+            break; 
+        }
+
+    iCurrentOperation = EIdle; 
+
+    TRACE_FUNC_EXIT
+    }
+
+// ---------------------------------------------------------------------------
+// Helper method for RunL() 
+// ---------------------------------------------------------------------------
+//
+TInt CATCOPSCmd::InspectModeAndProcessCommand()
+    {
+    TRACE_FUNC_ENTRY
+    TBuf<KMaxOperatorNameLength> buf;
+    TInt err; 
+
+    switch (iRegistrationMode)
+        {
+        case EModeAutomatic: 
+            AutomaticNetworkRegistration(); 
+            break; 
+        case EModeManual: 
+        case EModeManualAutomatic: // see also RunL() 
+            if(iParamArray.Count() < KMinimumParameterCountWhenOperatorPresent)
+                {
+                TRACE_FUNC_EXIT
+                return KErrArgument; 
+                }
+            
+            // At least the operator is present, so convert it into S60 format. 
+            buf.Copy(iParamArray[KOperatorParameterIndex]->Des()); 
+            err = ConvertOperatorToMccMnc(iDetectedNetworks, iFormat, buf, iMcc, iMnc); 
+            if(err != KErrNone)
+                {
+                TRACE_INFO(_L("CATCOPSCmd::HandleCommand() -- operator conversion failed!"));
+                TRACE_FUNC_EXIT
+                return KErrArgument; 
+                }
+
+            if (iParamArray.Count() >= KMinimumParameterCountWhenAccTechPresent) 
+                {
+                // Also access tech. is present. Convert it to ETel compatible value.  
+                TAccessTechnology accTech; 
+                TInt err = GetAccTechFromParameter(iParamArray[KAccessTechnologyParameterIndex]->Des(), accTech); 
+                if(err != KErrNone)
+                    {
+                    // Parameter problem, return an error. 
+                    TRACE_FUNC_EXIT
+                    return KErrArgument; 
+                    }
+                // Register both operator and access technology manually.
+                ManualNetworkRegistration(iMcc, iMnc, accTech); 
+                }
+            else 
+                {
+                // No access technology parameter, so register just the operator. 
+                ManualNetworkRegistration(iMcc, iMnc); 
+                }
+            break; 
+        case EModeDeregister: // Deregister from network 
+            // Not supported, return an error. 
+			TRACE_FUNC_EXIT
+            return KErrArgument; 
+        case EModeSetFormatParameter: 
+            // Storing format parameter was done already, so just reply OK. 
+            CreateReply(ETrue); 
+			TRACE_FUNC_EXIT
+            return KErrNone; 
+        default: 
+            return KErrArgument; 
+        }
+    TRACE_FUNC_EXIT
+    return KErrNone; 
+    }
+
+// ---------------------------------------------------------------------------
+// Converts the ETel access technology into 3GPP TS 27.007 V8.4.1 compatible format. 
+// ---------------------------------------------------------------------------
+//
+TInt CATCOPSCmd::SolveAccessTechnology(RMobilePhone::TMobilePhoneNetworkAccess &aAccessTech)
+    {
+    TRACE_FUNC_ENTRY
+
+    TUint caps;
+    if(iPacketService.GetStaticCaps(caps, RPacketContext::EPdpTypePPP) != KErrNone)
+        {
+        TRACE_FUNC_EXIT
+        return KErrGeneral;     
+        }
+
+    TRACE_INFO(( _L8("CATCOPSCmd::SolveAccessTechnology() static caps gotten (%b)"), caps));
+
+    switch(aAccessTech)
+        {
+        case RMobilePhone::ENetworkAccessGsm: 
+            if(caps & RPacketService::KCapsEGPRSSupported)
+                {
+                iAccTech = EGSMwithEGPRS; 
+                }
+            else 
+                {
+                iAccTech = EGSM; 
+                }
+            break; 
+        case RMobilePhone::ENetworkAccessGsmCompact: 
+            iAccTech = EGSMCompact; 
+            break; 
+        case RMobilePhone::ENetworkAccessUtran: 
+            if(caps & RPacketService::KCapsHSDPASupported) 
+                {
+                if(caps & RPacketService::KCapsHSUPASupported)
+                    {
+                    iAccTech = EUDMAwithHSDPAandHSUPA;  
+                    }
+                else 
+                    {
+                    iAccTech = EHSDPA;  
+                    }
+                }
+            else if(caps & RPacketService::KCapsHSUPASupported) 
+                {
+                iAccTech = EHSUPA;  
+                }
+            else 
+                {
+                iAccTech = EUDMA;  
+                }
+            break;
+        default: 
+            TRACE_INFO( _L("CATCOPSCmd::SolveAccessTechnology() unknown access tech!"));
+            iAccTech = EAccTechNotSet; 
+            return KErrArgument; 
+		}
+    TRACE_FUNC_EXIT
+    return KErrNone;  
+    }
+
+// ---------------------------------------------------------------------------
+// Contructs a response for the read command. 
+// ---------------------------------------------------------------------------
+//
+TInt CATCOPSCmd::ConstructNetworkInfoResponse()
+    {
+    TRACE_FUNC_ENTRY
+    RBuf8 reply;
+    TInt size(KMaxNetworkTestResponseAdditionalSize + KLongOperatorNameFormatLength);       
+    TChar carriageReturn;
+    TChar lineFeed;
+    TInt err;
+    err = reply.Create(size);
+	err |= iCallback->GetCharacterValue( ECharTypeCR, carriageReturn );
+	err |= iCallback->GetCharacterValue( ECharTypeLF, lineFeed );		
+	if(err != KErrNone) 
+		{
+		return err; 
+		}
+
+	// Some PC Software expects and extra CR+LF, hence those are added twice: 
+	reply.Append( carriageReturn ); 
+	reply.Append( lineFeed );
+    reply.Append( carriageReturn );
+    reply.Append( lineFeed );  
+    reply.Append(_L("+COPS: "));  
+    reply.AppendNum(iRegistrationMode);  
+    reply.Append(_L(","));  
+    switch(iFormat)
+        {
+        case RMmCustomAPI::EOperatorNameNitzFull:   
+            reply.AppendNum(EFormatLong);  
+            reply.Append(_L(",")); 
+            reply.Append(_L("\""));   
+            TRACE_INFO(( _L8("CATCOPSCmd::ConstructNetworkInfoResponse() appending (%S)"), 
+                    &iNetworkInfo.iLongName));
+            reply.Append(iNetworkInfo.iLongName);  
+            break; 
+        case RMmCustomAPI::EOperatorNameNitzShort:
+            reply.AppendNum(EFormatShort); 
+            reply.Append(_L(",")); 
+            reply.Append(_L("\"")); 
+            TRACE_INFO(( _L8("CATCOPSCmd::ConstructNetworkInfoResponse() appending (%S)"), 
+                    &iNetworkInfo.iShortName));
+            reply.Append(iNetworkInfo.iShortName); 
+            break; 
+        case RMmCustomAPI::EOperatorNameMccMnc: 
+            reply.AppendNum(EFormatNumeric); 
+            reply.Append(_L(",")); 
+            reply.Append(_L("\"")); 
+            TRACE_INFO(( _L8("CATCOPSCmd::ConstructNetworkInfoResponse() appending codes (%S) and (%S)"), 
+                            &iNetworkInfo.iCountryCode, &iNetworkInfo.iNetworkId));
+            reply.Append(iNetworkInfo.iCountryCode); 
+            reply.Append(iNetworkInfo.iNetworkId); 
+            break; 
+        }
+    reply.Append(_L("\"")); 
+
+    if(SolveAccessTechnology(iNetworkInfo.iAccess) == KErrNone && iAccTech != EAccTechNotSet) 
+        {
+        TRACE_INFO((_L("CATCOPSCmd::ConstructNetworkInfoResponse() appending acc. tech. (%d)"), 
+                            iAccTech));
+        reply.Append(_L(",")); 
+        reply.AppendNum(iAccTech); 
+        }
+
+    reply.Append( carriageReturn );
+    reply.Append( lineFeed );
+
+    CreateReply(ETrue, reply); 
+
+    TRACE_FUNC_EXIT
+    return KErrNone;  
+    }
+
+
+// ---------------------------------------------------------------------------
+// Contructs a response for the test command. 
+// ---------------------------------------------------------------------------
+//
+void CATCOPSCmd::ConstructNetworkListResponseL()
+    {
+    TRACE_FUNC_ENTRY
+    RBuf8 reply;
+    TChar carriageReturn;
+    TChar lineFeed;
+
+    TInt maxItemSize(KMaxNetworkReadResponseAdditionalSize  
+            + KShortOperatorNameFormatLength 
+            + KLongOperatorNameFormatLength 
+            + KNumericOperatorNameFormatLength
+            + KSupportedModesStr().Length()
+            + KSupportedFormatsStr().Length()); 
+
+    CleanupClosePushL(reply); 
+
+    User::LeaveIfNull(iDetectedNetworks); 
+    User::LeaveIfError(reply.Create( maxItemSize * iDetectedNetworks->Enumerate())); 
+    User::LeaveIfError(iCallback->GetCharacterValue( ECharTypeCR, carriageReturn ));
+    User::LeaveIfError(iCallback->GetCharacterValue( ECharTypeLF, lineFeed ));		
+
+    // Some PC Software expects and extra CR+LF, hence those are added twice: 
+    reply.Append( carriageReturn );
+    reply.Append( lineFeed );
+    reply.Append( carriageReturn );
+    reply.Append( lineFeed );
+    reply.Append( _L("+COPS: ") ); 
+
+    RMobilePhone::TMobilePhoneNetworkInfoV2 nwInfo; 
+    for(TInt i = 0; i < iDetectedNetworks->Enumerate(); ++i)
+        {
+		if(i > 0) // Add CR+LF after the first cycle. 
+			{
+            reply.Append( carriageReturn );
+            reply.Append( lineFeed );
+			}
+        nwInfo = iDetectedNetworks->GetEntryL(i);  
+
+        reply.Append(_L("(")); 
+        reply.AppendNum(nwInfo.iStatus); 
+        reply.Append(_L(",")); 
+		reply.Append(_L("\"")); 
+        reply.Append(nwInfo.iLongName); 
+		reply.Append(_L("\"")); 
+        reply.Append(_L(",")); 
+		reply.Append(_L("\"")); 
+        reply.Append(nwInfo.iShortName); 
+		reply.Append(_L("\"")); 
+        reply.Append(_L(",")); 
+		reply.Append(_L("\"")); 
+        reply.Append(nwInfo.iCountryCode); 
+        reply.Append(nwInfo.iNetworkId); 
+		reply.Append(_L("\"")); 
+        if(SolveAccessTechnology(nwInfo.iAccess) == KErrNone && iAccTech != EAccTechNotSet) 
+            {
+            TRACE_INFO((_L("CATCOPSCmd::ConstructNetworkListResponse() appending acc. tech. (%d)"), iAccTech));
+            reply.Append(_L(",")); 
+            reply.AppendNum(iAccTech); 
+            }
+        reply.Append(_L(")")); 
+        reply.Append(_L(",")); 
+		TRACE_INFO( _L("CATCOPSCmd::ConstructNetworkListResponse() -- entry added to reply."));
+        }
+    reply.Append(KSupportedModesStr); // Supported modes as defined in 3GPP TS 27.007 V8.4.1
+    reply.Append(KSupportedFormatsStr);  // Supported formats as defined in 3GPP TS 27.007 V8.4.1
+
+    reply.Append( carriageReturn );
+    reply.Append( lineFeed );
+
+		// Finally append the "OK". CreateOkOrErrorReply returns verbose or numeric version. 
+    RBuf8 okReply;
+    CleanupClosePushL(okReply); 
+    iCallback->CreateOkOrErrorReply( okReply, ETrue );
+    reply.Append( okReply);
+    CreateReply(ETrue, reply); 
+    CleanupStack::PopAndDestroy(&okReply);   
+    CleanupStack::PopAndDestroy(&reply);   
+    TRACE_FUNC_EXIT
+    }
+
+// ---------------------------------------------------------------------------
+// Finalises the response and sends it. 
+// ---------------------------------------------------------------------------
+//
+void CATCOPSCmd::CreateReply(TBool aIsOK, const TDesC8 &aReply) 
+    {
+    if(aIsOK == EFalse)
+        {
+        iCallback->CreateReplyAndComplete( EReplyTypeError);
+        }
+    else 
+        {
+        if(aReply.Length() > 0)
+            {
+            iCallback->CreateReplyAndComplete( EReplyTypeOther,
+                                           aReply );
+            }
+        else 
+            {
+            iCallback->CreateReplyAndComplete( EReplyTypeOk);
+            }
+        }
+    iCurrentOperation = EIdle; 
+    TRACE_FUNC_EXIT
+    }