atcommands/modematplugin/src/atcopscmd.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Wed, 09 Jun 2010 09:52:54 +0300
branchRCL_3
changeset 35 95674584745d
parent 13 b6f55cd40afd
permissions -rw-r--r--
Revision: 201021 Kit: 2010123

/*
* 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(); 
	    TRACE_FUNC_EXIT
        return; 
	    }
    // 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: 
			if(iRegistrationMode == EModeManualAutomatic)
                {
                // Manual registration failed, try automatic next. 
                TRACE_INFO( _L("CATCOPSCmd::RunL() registration mode manual automatic, try automatic."));
                AutomaticNetworkRegistration(); 
				break;
                }
            else 
                {
				// 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
    }