/*****************************************************************************
 * Copyright (C) Gemalto M2M GmbH
 *****************************************************************************
 * Project: IMC-RIL-Driver
 *****************************************************************************
 * Author : Wireless Modules RD SW A2
 *****************************************************************************
 * $Workfile: Y:/z_ms-d/src/working/Quinn/RilDrv/network.cpp $
 * $Revision: /main/ms-d/97 $
 *     $Date: 2014-01-31 12:59:40 $
 *   $Author: pmackent $
 *****************************************************************************
 * Target:
 *  Microsoft Windows Mobile 5.0
 *****************************************************************************
 * Description:
 *  Gemalto IMC RIL
 *
 *  Notes:
 *  Long operator names in the OPERATORNAMEMAP should not exceeed 16 characters in
 *  length, otherwise visual clipping of the operator name string may occur in the
 *  user interface.  The OPERATORNAMEMAP entries must be numerically ordered.
 *
 *  In this sample code, all PCS 1900-based networks have duplicate or triplicate
 *  entries listed for each operator due to the three-digit MNCs sometimes used in
 *  the PCS spectrum.  For example, PLMN A is a PCS network and is frequently seen
 *  in various SIM EF_PLMNsel tables as "123 45", "123 045", or "123 450", depending
 *  on what equipment last wrote to that table.  To cover each instance, the table
 *  can list all three instances and map them to the same "PLMN A" long operator
 *  name string.  In the case that two different PCS networks could ambigouosly have
 *  an overlapping number, the network with higher numeric rank is labeled with the
 *  overlapping operator code.  For example, PLMN B is "111 01" and PLMN C is
 *  "111 10"; the table would list the following:
 *      { 11101,    "PLMN B" },
 *      { 11110,    "PLMN C" },
 *      { 111001,   "PLMN B" },
 *      { 111010,   "PLMN C" },
 *      { 111100,   "PLMN C" },
 *  It is highly suggested (but not required) to follow this duplicate and
 *  triplicate PCS listing practice in order to provide better operator matches.
 *****************************************************************************
 *
 * $Log: Y:/z_ms-d/src/working/Quinn/RilDrv/network.cpp$ 
 * 
 * /main/ms-d/97   2014-01-31 12:59:40   pmackent 
 * Retries for operator query
 *
 * /main/ms-d/96   2013-09-23 10:22:02   pmackent
 * Fix exception in get operator list (IPIS100091582)
 *
 * /main/ms-d/95   2013-06-20 14:19:08   pmackent
 * Remove unnecessary blanks in parsing AT^SXEONS command
 *
 * /main/ms-d/94   2013-05-24 13:58:04   pmackent
 * Module changes to multimode after AT+COPS=1
 *
 * /main/ms-d/93   2013-04-22 16:12:23   pmackent
 * Work on workarounds for AT+COPS/<AcT> and AT^SJDL/ATE
 *
 * /main/ms-d/92   2013-04-12 09:21:47   pmackent
 * Adaptions for PXS8 2.910
 *
 * /main/ms-d/91   2013-03-04 13:19:44   pmackent
 * Adaptions for EHS5 1.300
 *
 * /main/ms-d/90   2013-02-26 15:07:41   pmackent
 * Adaptions for EHS5 1.290
 *
 * /main/ms-d/89   2013-01-30 16:01:40   pmackent
 * AT$MDN has been changed to AT$MDN?
 *
 * /main/ms-d/88   2013-01-18 10:18:00   pmackent
 * Get CDMA numeric network name from AT^SMONI
 *
 * /main/ms-d/87   2012-12-12 15:24:10   pmackent
 * Workarounds for bugs in PXS8 2.821
 *
 * /main/ms-d/86   2012-10-08 16:19:39   pmackent
 * Comply to Klocwork checks.
 *
 * /main/ms-d/85   2012-09-04 15:49:10   pmackent
 * Handle wrong "SIM failure" error for AT+CPOL write command
 *
 * /main/ms-d/84   2012-08-29 14:35:56   pmackent
 * Workaround for COPS changes SCFG bug
 *
 * /main/ms-d/83   2012-07-10 13:16:17   pmackent
 * Rearrange lines to satisfy prefast
 *
 * /main/ms-d/82   2012-07-10 11:50:27   pmackent
 * Remove unused static variable
 *
 * /main/ms-d/81   2012-07-05 14:42:50   pmackent
 * Use byte allignment required by Arm processor
 *
 * /main/ms-d/80   2012-07-05 10:12:10   pmackent
 * Introduce loading of network provider list
 *
 * /main/ms-d/79   2012-07-03 11:51:52   pmackent
 * Introduce infor about provider list date
 *
 * /main/ms-d/78   2012-06-07 13:58:53   pmackent
 * Adaptions for multimode
 * Workaround for CREG=0 bug in PXS8
 *
 * /main/ms-d/77   2012-06-05 11:27:36   pmackent
 * Don't send AT+COPS=2 while registered to CDMA
 *
 * /main/ms-d/76   2012-04-25 09:58:10   pmackent
 * Corrected network name key
 *
 * /main/ms-d/75   2012-04-18 13:47:07   ctna0256
 * Introducing PVS8
 * Implementing CDMA-SMS
 * Various CDMA enhancements
 *
 * /main/ms-d/74   2012-02-29 10:07:57   ctna0256
 * Add country code to operator name, if it is missing
 *
 * /main/ms-d/73   2012-02-28 14:42:56   ctna0256
 * Solving problem with format of network names
 *
 * /main/ms-d/72   2012-02-28 11:00:47   ctna0256
 * Skip over unnecessary COPS parameters in EHS5
 *
 * /main/ms-d/71   2012-02-27 16:26:43   ctna0256
 * Skip unnecessary lines in AT+CPOL?
 *
 * /main/ms-d/70   2012-02-24 16:17:07   ctna0256
 * Use UCS2 for Intel manual network selection
 *
 * /main/ms-d/69   2012-02-22 13:26:54   ctna0256
 * Encapsulate use of Intel AT command set
 *
 * /main/ms-d/68   2012-02-16 12:55:27   ctna0256
 * Disable prefast warning in Microsoft code
 *
 * /main/ms-d/67   2012-02-14 14:52:30   ctna0256
 * Improved comments
 *
 * /main/ms-d/66   2012-02-03 15:07:19   ctna0256
 * Move URC parsing functions in separate file
 *
 * /main/ms-d/65   2012-02-03 14:01:13   ctna0256
 * Phonebook and SIM access now in separate files
 *
 * /main/ms-d/64   2012-01-31 16:38:42   ctna0256
 * Added support for EHS5
 *
 * /main/ms-d/63   2011-10-07 16:11:44   ctna0256
 * Small corrections
 *
 * /main/ms-d/62   2011-09-19 14:10:38   ctna0256
 * New registry key for no SOPS on EONS
 *
 * /main/ms-d/61   2011-09-19 10:26:22   ctna0256
 * Workaround for wrong CME-Error at AT+CPOL on PH8
 *
 * /main/ms-d/60   2011-05-30 09:36:04   ctna0256
 * Ignore wrong CREG:0 after CREG:3 (IPIS100073002)
 *
 * /main/ms-d/59   2011-02-02 10:58:18   ctna0256
 * Earlier CSQ notifications
 *
 * /main/ms-d/58   2010-12-10 14:41:32   ctna0256
 * Workaround for COPS1 problem
 *
 * /main/ms-d/57   2010-11-17 13:44:26   ctna0256
 * Handle new <AcT> values for COPS
 *
 * /main/ms-d/56   2010-10-25 11:33:41   ctna0256
 * Workaround for wrong +CREG URC after COPS=2
 *
 * /main/ms-d/55   2010-09-21 13:21:53   ctna0256
 * Prevent early use of get operator list
 *
 * /main/ms-d/54   2010-07-06 10:00:23   ctna0256
 * Handle case of data only SIM
 *
 * /main/ms-d/53   2010-06-22 15:55:18   ctna0256
 * Remove useless blank in PH8 numeric operator name
 *
 * /main/ms-d/52   2010-06-22 15:16:00   ctna0256
 * New format for numeric network name in PH8
 *
 * /main/ms-d/51   2010-03-05 12:57:14   ctna0256
 * Prevent multiple get operator list commands
 *
 * /main/ms-d/50   2010-02-17 14:26:48   ctna0256
 * GetRegistrySZ() might return TRUE, even on error (IPIS100062294)
 *
 * /main/ms-d/49   2009-09-28 13:21:27   ctna0256
 * Use no-roaming setting for network state query
 *
 * /main/ms-d/48   2009-09-04 11:14:38   ctna0256
 * Unregister from network before registering manually (IPIS100057521)
 *
 * /main/ms-d/47   2009-05-27 10:05:06   ctna0256
 * Deregister module in PUK blocked state caused by wrong PIN change
 *
 * /main/ms-d/46   2009-04-08 11:37:29   ctna0256
 * Fast registration
 *
 * /main/ms-d/45   2009-03-03 10:51:42   ctna0256
 * Workaround for HC25 roaming problem
 *
 * /main/ms-d/44   2009-02-23 13:16:11   ctna0256
 * Avoid unwanted E_FAIL error codes
 *
 * /main/ms-d/43   2009-02-18 14:12:28   ctna0256
 * Removed unnecessary debug prints
 *
 * /main/ms-d/42   2009-02-18 10:49:45   ctna0256
 * Addition include file for WM65
 *
 * /main/ms-d/41   2009-02-18 09:39:45   ctna0256
 * New WM65 API
 *
 * /main/ms-d/40   2008-10-22 15:30:32   ctna0256
 * Deleted unused declaration
 *
 * /main/ms-d/39   2008-09-18 15:34:02   ctna0256
 * Allow compiling of RIL on WM5
 *
 * /main/ms-d/38   2008-08-22 10:24:42   ctna0256
 * Set Cinterion copyright text
 *
 * /main/ms-d/37   2008-08-21 09:34:53   ctna0256
 * Prepared for new disclaimer handling
 *
 * /main/ms-d/36   2008-07-31 10:36:15   mp49210
 * Check for 6-digit operator number, if 5-digit number cannot be found
 *
 * /main/ms-d/35   2008-07-22 15:52:56   mp49210
 * Complience to WM7, Lint and PreFast requirements
 *
 * /main/ms-d/34   2008-06-12 11:38:25   mp49210
 * Workaround for wrong characters in EONS name
 *
 * /main/ms-d/33   2008-06-06 15:31:06   mp49210
 * Handling of additional CPOL parameters
 *
 * /main/ms-d/32   2008-04-29 13:34:31   mp49210
 * Changed error code for reading own numbers when module is off
 *
 * /main/ms-d/31   2008-04-24 10:42:37   mp49210
 * New options for display service provider name
 *
 * /main/ms-d/30   2008-04-21 08:16:45   mp49210
 * Handling of AT&Ts network selection menue change
 *
 * /main/ms-d/29   2008-04-15 11:24:32   mp49210
 * Fix for wrong service provider name detection
 *
 * /main/ms-d/28   2008-04-10 15:21:37   mp49210
 * New optional handling of service provider name
 *
 * /main/ms-d/27   2008-04-03 14:41:43   mp49210
 * Introduction of new debug zones
 *
 * /main/ms-d/26   2008-02-19 11:33:11   mp49210
 * Handle COPS/AcT parameter sent by the MMI during startup of the RIL
 *
 * /main/ms-d/25   2008-02-08 13:56:28   mp49210
 * Allow compiling with WM5
 *
 * /main/ms-d/24   2008-02-01 15:34:59   mp49210
 * End data connection when query list of available operators
 *
 * /main/ms-d/23   2007-11-21 13:35:03   mp49210
 * Lint compatibility
 *
 * /main/ms-d/22   2007-11-10 13:18:07   mp49210
 * Fixed potentional NULL pointer access
 *
 * /main/ms-d/21   2007-11-09 16:29:15   mp49210
 * Workaround for irritating error on AT+COPS=1
 *
 * /main/ms-d/20   2007-10-09 16:18:12   mp49210
 * Workaround for reset of AcT after radio band group selection
 *
 * /main/ms-d/19   2007-09-21 10:29:41   mp49210
 * Introduction WM6 specific AcT selection (optional)
 *
 * /main/ms-d/18   2007-09-10 15:20:27   mp49210
 * Corrected check for EONS type in operator list
 *
 * /main/ms-d/17   2007-07-10 13:43:56   mp49210
 * Workaround for blocking AT interface after early AT+COPS=?
 *
 * /main/ms-d/16   2007-07-10 09:28:59   mp49210
 * Introduction of new Radio/Band/Group parameter
 *
 * /main/ms-d/15   2007-07-04 07:32:10   mp49210
 * Introduction of radio band group search
 *
 * /main/ms-d/14   2007-06-11 11:41:11   mp49210
 * Correct use of AT^SNWS/+COPS workarounds
 *
 * /main/ms-d/13   2007-05-25 11:43:04   mp49210
 * Workaround for changed band settings by AT^SNWS
 *
 * /main/ms-d/12   2007-05-23 08:37:34   mp49210
 * Introduction of AT^SNWS command (optional)
 *
 * /main/ms-d/11   2007-03-29 10:22:47   mp49210
 * Prevent exception while parsing error messages when module is off
 *
 * /main/ms-d/10   2007-03-28 13:48:04   mp49210
 * Remove 'Classic RIL' specific code and comments
 *
 * /main/ms-d/9   2007-03-01 10:37:53   mp49210
 * Workaround for inaccessible UPPLMN on USIMs
 *
 * /main/ms-d/8   2007-02-09 15:15:15   mp49210
 * Lint compatibility
 *
 * /main/ms-d/7   2007-01-30 15:20:34   mp49210
 * Change to new numeric provider name format
 *
 * /main/ms-d/6   2006-12-20 16:56:58   mp49210
 * New syntax of AT^SOPS=?
 *
 * /main/ms-d/5   2006-12-19 18:13:11   mp49210
 * Some changes for commands used during initialization
 *
 * /main/ms-d/4   2006-11-09 14:24:47   mp49210
 * Quinn specific feature: selction of 2G / 3G network
 *
 * /main/ms-d/3   2006-11-07 10:18:47   mp49210
 * Substitute AT^SPLR and AT^SPLW
 *
 * /main/ms-d/2   2006-11-07 09:38:56   mp49210
 * New syntax of AT+COPS
 *
 * /main/ms-d/1   2006-10-25 16:54:06   wm36264
 * First transformation from classic RIL
 *
*/

//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2008 Gemalto M2M GmbH
// Copyright (C) 2006-2008 Siemens AG
// Copyright (C) 2003 Intrinsyc Europe Ltd.
//
// Version: 1.0
// Date:    dummy
//
//////////////////////////////////////////////////////////////////////////////

/*++
THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
PARTICULAR PURPOSE.
Copyright (c) 1995-1999  Microsoft Corporation
--*/

#include "precomp.h"
#if defined(IOCTL_RIL_GETSMSNLTABLELIST)
#include "regext.h"
#endif

#define OPLIST_COPS     1
#define OPLIST_SOPS     2
#define OPLIST_SNWS     3
#ifdef SUPPORTS_EHSX_R1
#define OPLIST_XEONS    4
#endif
#define OPLIST_SXEONS   5

#define COPS2_DEFAULT_DELAY   2000
#define COPS1_DEFAULT_DELAY   3000

UINT g_iLastCOPS = 0;
UINT g_iLastCOPSOpr = 0;
UINT g_iSetCOPSinInit = NO_INIT_COPS_SETTING;
char g_szSetCOPSinInitOperator[MAXLENGTH_OPERATOR_NUMERIC+1];
#ifdef RIL_WM6_ACT_SELECTION
UINT g_iSetCOPSinInitParams = 0;
#endif
char g_szLastKnownEONSNameLong[MAXLENGTH_OPERATOR_LONG+1];
char g_szLastKnownEONSNameShort[MAXLENGTH_OPERATOR_LONG+1]; // Short name for simulated EONS might be as long as short names
BOOL g_bNeedAllBandNetworkSelection = FALSE;
BOOL g_bFakeRegStatusDeniedToHome = FALSE;
DWORD g_dwLastKnowBandSelection = 127;
DWORD g_dwAutomaticGroupSearchTimeout = 0;

// Buffer for the last known preferred operator list.
UINT g_iPPLMNBufferPos = PPLMN_BUFFER_NOT_FILLED;
static UINT g_PPLMNBuffer[MAX_PPLMN_BUFFER];
static UINT g_PPLMNBufferParam[MAX_PPLMN_BUFFER];
CRITICAL_SECTION g_PPLMNBufferSection;

// Buffer for the last known operator list. The buffer will be kept until
// a new operator list is know.
static UINT g_cbOperatorListBlob = 0;
static BYTE *g_pOperatorListBlob = NULL;

// Bug in Microsoft's MMI: the "Set Preferred Networks" dialog is not handling the case correctly,
// when a network is configured for only one technology, GSM or UMTS. If the following define
// is set, the RIL will enable all entries of the preferred operator list for both technology, even
// when only one is selected.
#define  FORCE_UMTS_PLUS_GSM

#define UNKNOWN_COUNTRY_CODE ("--")
#define MCC_LEN (3)

//
// Long operator names
//
static const OPERATORNAMEMAP g_rgonmOperatorNamesInternal[] =
{
#ifdef USE_MODULE_TABLE_OF_OPERATORS
    { 00000,    "--",          UNKNOWN_COUNTRY_CODE },  // Dummy entry
#else
#include "network.lst"
#endif
};
#define NUM_OPERATORNAMES_INTERNAL   (sizeof(g_rgonmOperatorNamesInternal) / sizeof(g_rgonmOperatorNamesInternal[0]))

// Pointer to the buffers containing the network operator list. The list might
// be the internal list compiled into the RIL or a list the RIL has been read
// from a file.
const OPERATORNAMEMAP *g_rgonmOperatorNames = g_rgonmOperatorNamesInternal;
DWORD g_nNumOperatorNames  = NUM_OPERATORNAMES_INTERNAL;
static BOOL s_fListFromFile = FALSE;
static WCHAR s_wszNwlListFile[MAX_PATH];
static char *s_szOperatorNamesStringBuffer = NULL;

//
// Services
//
static const DWORD g_rgdwServices[] =
{
    RIL_SERVICE_MODEM_ASYNC,            // 1
    RIL_SERVICE_MODEM_SYNC,             // 2
    RIL_SERVICE_PADACCESS_ASYNC,        // 3
    RIL_SERVICE_PACKETACCESS_SYNC,      // 4
    RIL_SERVICE_VOICE,                  // 5
};
#define NUM_SERVICES    (sizeof(g_rgdwServices) / sizeof(DWORD))


//
// Operator status values
//
static const DWORD g_rgdwOperStats[] =
{
    RIL_OPSTATUS_UNKNOWN,       // 0
    RIL_OPSTATUS_AVAILABLE,     // 1
    RIL_OPSTATUS_CURRENT,       // 2
    RIL_OPSTATUS_FORBIDDEN,     // 3
};
#define NUM_OPERSTATS   (sizeof(g_rgdwOperStats) / sizeof(DWORD))


//
// Operator selection modes
//
static const DWORD g_rgdwOperSelModes[] =
{
    RIL_OPSELMODE_AUTOMATIC,        // 0
    RIL_OPSELMODE_MANUAL,           // 1
    (DWORD)-1,                      // 2
    (DWORD)-1,                      // 3
    RIL_OPSELMODE_MANUALAUTOMATIC,  // 4
};
#define NUM_OPSELMODES  (sizeof(g_rgdwOperSelModes) / sizeof(DWORD))




//
// Comparison routine used for binary search below
//
static int _cdecl BSCompareOperatorNames(const void* pElem1, const void* pElem2)
{
    int nRet;
    UINT nNumericName1 = ((OPERATORNAMEMAP*)pElem1)->nNumericName;
    UINT nNumericName2 = ((OPERATORNAMEMAP*)pElem2)->nNumericName;

    if (nNumericName1 < nNumericName2) {
        nRet = -1;
    } else if (nNumericName1 == nNumericName2) {
        nRet = 0;
    } else {
        nRet = 1;
    }
    return nRet;
}

//
// Like BSCompareOperatorNames(), but unly compares the countries.
//
static int _cdecl BSCompareOperatorCountries(const void* pElem1, const void* pElem2)
{
    int nRet;
    UINT nNumericName1 = ((OPERATORNAMEMAP*)pElem1)->nNumericName;
    UINT nNumericName2 = ((OPERATORNAMEMAP*)pElem2)->nNumericName;

    // Numbers can have 5 or 6 digits, use only the first 3 digits.
    nNumericName1 = nNumericName1 >= 100000 ? nNumericName1 / 1000 : nNumericName1 / 100;
    nNumericName2 = nNumericName2 >= 100000 ? nNumericName2 / 1000 : nNumericName2 / 100;

    if (nNumericName1 < nNumericName2) {
        nRet = -1;
    } else if (nNumericName1 == nNumericName2) {
        nRet = 0;
    } else {
        nRet = 1;
    }
    return nRet;
}

//
// MakeLongNameFromNumericName()
// This function is called, when there is no correct long name, so the RIL
// numst contruct one base on the numeric network name.
//
static void MakeLongNameFromNumericName(char *pszLongName, char *pszNumName, UINT cbLen)
{
    UINT nLen;

    // Generate the long name in the required form.
    nLen = strlen(pszNumName);
    if (nLen >= 5 && nLen <= 6) {
        // Note: the "CC xxx NC yy" form is obsolete now, it is only
        // kept for compatibility with the HC25.
        if (SHORT_NUMERIC_NETWORK_NAME)
            (void)_snprintfz(pszLongName, cbLen, "%c%c%c %s", pszNumName[0], pszNumName[1], pszNumName[2], pszNumName+3);
        else
            (void)_snprintfz(pszLongName, cbLen, "CC %c%c%c NC %s", pszNumName[0], pszNumName[1], pszNumName[2], pszNumName+3);
    }
    else {
        // Default: just copy the numeric name. This should never happen,
        // except we are on a GSM tester which might give country codes
        // not found in the real life.
        if (strlen(pszLongName) == 0) {
            if (strcmp(pszNumName, "101") == 0)   // Special case: default R&S/Agilent setting
                (void)strncpyz(pszLongName, "001 01", cbLen);
            else
                (void)strncpyz(pszLongName, pszNumName, cbLen);
        }
    }
}

//
// GetProviderListInfo()
// The provider list may contain an information string containing the creation
// date. This function returns this info string.
// Parameter:
// szInfo [out]: string containing the list info, must hold at least
//                    PROVIDERLIST_DATE_STRING_LEN chars.
// Return value: TRUE, if the info string is present in the current list.
//
BOOL GetProviderListInfo(char *szInfo)
{
    OPERATORNAMEMAP* ponmFound;
    OPERATORNAMEMAP onmKey;

    if (szInfo == NULL)
        return FALSE;

    memset(&onmKey, 0x00, sizeof(onmKey));
    memset(szInfo, 0, PROVIDERLIST_DATE_STRING_LEN);

   onmKey.nNumericName = PROVIDERLISTINFOKEY;
   ponmFound = (OPERATORNAMEMAP*)bsearch(&onmKey, g_rgonmOperatorNames, g_nNumOperatorNames, sizeof(OPERATORNAMEMAP), BSCompareOperatorNames);
   if (ponmFound == NULL)
       return FALSE;
   (void)strncpyz(szInfo, ponmFound->szLongName, PROVIDERLIST_DATE_STRING_LEN);
   return TRUE;
}

//
// GetLongOperatorName()
// This function fills in all the operator name in the RILOPERATORNAMES
// structure based on the numeric operator name. There might be already
// an EONS based operator name present.
// Long operator name precedence:
// 1) Value in registry, keyed by network number.
// 2) Value from radio response (if EONS is enabled).
// 3) Value in hard-coded table above.
// 4) Value from radio response (if EONS is disabled).
// 5) Numeric value.
//
// Parameters:
// pronNames [in,out]: pointer to structure with operator names
// bEONSactive [in]: if set to TRUE, the long name in pronNames is an EONS name
//
static void GetLongOperatorName(RILOPERATORNAMES* pronNames, BOOL bEONSactive)
{
    OPERATORNAMEMAP onmKey;
    OPERATORNAMEMAP* ponmFound;
    TCHAR tszLongName[MAXLENGTH_OPERATOR_LONG];

    memset(tszLongName, 0, sizeof(tszLongName));

    // Make sure we have a numeric name
    if ((RIL_PARAM_ON_NUMNAME & pronNames->dwParams)) {
        // Now see if we have a mapping for this numeric name specified in the registry
        if (GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyOperNames, TString(pronNames->szNumName), tszLongName, MAXLENGTH_OPERATOR_LONG) && _tcslen(tszLongName) > 0) {
            // Yes, we do -- use it
            (void)strncpyz(pronNames->szLongName, AnsiString(tszLongName), MAXLENGTH_OPERATOR_LONG);
            pronNames->dwParams |= RIL_PARAM_ON_LONGNAME;
        } else {
            // Set up the key for binary search
            memset(&onmKey, 0x00, sizeof(onmKey));

            // Perform binary search on the operator name map

            //direct search first
            onmKey.nNumericName = atoi(pronNames->szNumName);
            ponmFound = (OPERATORNAMEMAP*)bsearch(&onmKey, g_rgonmOperatorNames, g_nNumOperatorNames, sizeof(OPERATORNAMEMAP), BSCompareOperatorNames);
            if ((NULL == ponmFound) && (6 == strlen(pronNames->szNumName))) {
                //handle 2 unusual PCS 1900 cases..
                DWORD dwTemp;
                //try the YYY0XX case first...(this needs to be done first because of proper ambiguity resolution)
                onmKey.nNumericName = atoi(pronNames->szNumName);
                dwTemp = onmKey.nNumericName % 100;
                onmKey.nNumericName -= dwTemp;
                onmKey.nNumericName /= 10;
                onmKey.nNumericName += dwTemp;
                ponmFound = (OPERATORNAMEMAP*)bsearch(&onmKey, g_rgonmOperatorNames, g_nNumOperatorNames, sizeof(OPERATORNAMEMAP), BSCompareOperatorNames);
                if (NULL == ponmFound) {
                    //try the YYYXX_ case next...
                    onmKey.nNumericName = atoi(pronNames->szNumName);
                    onmKey.nNumericName /= 10;
                    ponmFound = (OPERATORNAMEMAP*)bsearch(&onmKey, g_rgonmOperatorNames, g_nNumOperatorNames, sizeof(OPERATORNAMEMAP), BSCompareOperatorNames);
                }
            }
            if ((NULL == ponmFound) && (5 == strlen(pronNames->szNumName))) {
                if (NULL == ponmFound) {
                    //try the YYYXX0 case next...
                    onmKey.nNumericName = atoi(pronNames->szNumName);
                    onmKey.nNumericName *= 10;
                    ponmFound = (OPERATORNAMEMAP*)bsearch(&onmKey, g_rgonmOperatorNames, g_nNumOperatorNames, sizeof(OPERATORNAMEMAP), BSCompareOperatorNames);
                }
            }
            if (ponmFound) {
                // Copy the found long name into the provided buffer (do not do it, if EONS is active and a name is already present)
                if ((!(RIL_PARAM_ON_LONGNAME & pronNames->dwParams)) || !bEONSactive)
                    (void)strncpyz(pronNames->szLongName, ponmFound->szLongName, MAXLENGTH_OPERATOR_LONG);
                // We use the country code anyway.
                (void)strncpyz(pronNames->szCountryCode, ponmFound->szCountryCode, MAXLENGTH_OPERATOR_COUNTRY_CODE);
                pronNames->dwParams |= RIL_PARAM_ON_LONGNAME|RIL_PARAM_ON_COUNTRY_CODE;
            }
        }

        // If we still don't have a long name, then use the numeric name to generate one.
        if (!(RIL_PARAM_ON_LONGNAME & pronNames->dwParams))
        {
            MakeLongNameFromNumericName(pronNames->szLongName, pronNames->szNumName, MAXLENGTH_OPERATOR_LONG);
            pronNames->dwParams |= RIL_PARAM_ON_LONGNAME;
        }

        // If the country code is missing, try to find the correct one. If the
        // country code cannot be found, set to "unknown country".
        if (!(pronNames->dwParams & RIL_PARAM_ON_COUNTRY_CODE)) {
            // Perform search to find any other operator with the same MCC.
            memset(&onmKey, 0x00, sizeof(onmKey));
            onmKey.nNumericName = atoi(pronNames->szNumName);
            ponmFound = (OPERATORNAMEMAP*)bsearch(&onmKey, g_rgonmOperatorNames, g_nNumOperatorNames, sizeof(OPERATORNAMEMAP), BSCompareOperatorCountries);
            if (ponmFound != NULL)
                (void)strncpyz(pronNames->szCountryCode, ponmFound->szCountryCode, MAXLENGTH_OPERATOR_COUNTRY_CODE);
            else
                (void)strncpyz(pronNames->szCountryCode, UNKNOWN_COUNTRY_CODE, MAXLENGTH_OPERATOR_COUNTRY_CODE);
            pronNames->dwParams |= RIL_PARAM_ON_COUNTRY_CODE;
        }

        // Last check: the module might have given the numeric name as the long
        // name. This is a problem, because the numeric name doesn't contain a
        // blank between MCC and MNC as required by AT&T.
        if (CHECK_FOR_NUMERIC_NETWORK_NAME) {
            // Compare numeric and long name.
            if (strcmp(pronNames->szLongName, pronNames->szNumName) == 0) {
                // Generate the long name in the required form.
                MakeLongNameFromNumericName(pronNames->szLongName, pronNames->szNumName, MAXLENGTH_OPERATOR_LONG);
            }
        }
    }
}

//
//
//
static HRESULT ParseGetSubscriberNumbers(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    UINT i;
    UINT nValue;
    UINT nUsed = 0;
    UINT nAllocated = 0;
    DWORD dwCurrentAddressId = 0;
    char szAddress[MAXLENGTH_ADDRESS];
    RILSUBSCRIBERINFO* rgrsi = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "+CNUM: "
    while (MatchStringBeginning(szRsp, "+CNUM: ", szRsp)) {
        if (nUsed == nAllocated) {
            if (!AllocateOrReallocateStorage((BYTE**)&rgrsi, sizeof(RILSUBSCRIBERINFO), nUsed, &nAllocated, NETWK_ALLOC_NUMBER)) {
                hr = E_OUTOFMEMORY;
                goto Error;
            }
        }

        memset(&rgrsi[nUsed], 0x00, sizeof(RILSUBSCRIBERINFO));
        rgrsi[nUsed].cbSize = sizeof(RILSUBSCRIBERINFO);

        // Parse "<alpha>"
        if (ParseQuotedEncodedString(ENCODING_UCS2, szRsp, rgrsi[nUsed].wszDescription, rgrsi[nUsed].wszDescription + MAXLENGTH_DESCRIPTION)) {
            rgrsi[nUsed].dwParams |= RIL_PARAM_SI_DESCRIPTION;
        }

        // Parse ",<number>,<type>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)                     ||
            !ParseString(szRsp, szAddress, MAXLENGTH_ADDRESS, szRsp)     ||
            !MatchStringBeginning(szRsp, ",", szRsp)                     ||
            !ParseUIntAndVerifyAbove(szRsp, FALSE, 0x100, nValue, szRsp) ||
            !StringToRILAddress(szAddress, (BYTE)nValue, rgrsi[nUsed].raAddress)) {
            goto Continue;
        }
        rgrsi[nUsed].dwParams |= RIL_PARAM_SI_ADDRESS;

        // Some radios are giving us empty +CNUM responses.
        // Don't return these (and don't include in multiline count).
        if (('\0' == szAddress[0]) &&
            (0 == (rgrsi[nUsed].dwParams & RIL_PARAM_SI_DESCRIPTION)))
        {
            goto Continue;
        }

        // This assignment is a heuristic that seems correct.
        rgrsi[nUsed].dwAddressId = dwCurrentAddressId++;
        rgrsi[nUsed].dwParams |= RIL_PARAM_SI_ADDRESSID;

        // Parse ","
        if (MatchStringBeginning(szRsp, ",", szRsp)) {
            // Parse "<speed>"
            if (!ParseUInt(szRsp, TRUE, nValue, szRsp)) {
                goto Continue;
            }
            for (i = 0; i < NUM_CONNSPEEDS; i++) {
                if (nValue == g_rgdwConnSpeeds[i]) {
                    rgrsi[nUsed].dwSpeed = i;
                    break;
                }
            }
            if (NUM_CONNSPEEDS == i) {
                // We couldn't match the response with anything
                rgrsi[nUsed].dwSpeed = RIL_SPEED_UNKNOWN;
            }
            rgrsi[nUsed].dwParams |= RIL_PARAM_SI_SPEED;

            // Parse ",<service>
            if (!MatchStringBeginning(szRsp, ",", szRsp) ||
                !ParseUInt(szRsp, TRUE, nValue, szRsp)) {
                goto Continue;
            }
            if (NUM_SERVICES > nValue) {
                rgrsi[nUsed].dwService = g_rgdwServices[nValue];
            } else {
                rgrsi[nUsed].dwService = RIL_SERVICE_UNKNOWN;
            }
            rgrsi[nUsed].dwParams |= RIL_PARAM_SI_SERVICE;

            // Parse ","
            if (MatchStringBeginning(szRsp, ",", szRsp)) {
                // Parse "<itc>"
                if (!ParseUIntAndVerifyAbove(szRsp, TRUE, 2, nValue, szRsp)) {
                    goto Continue;
                }
                rgrsi[nUsed].dwITC = (nValue ? RIL_ITC_UDI : RIL_ITC_31KHZ);
                rgrsi[nUsed].dwParams |= RIL_PARAM_SI_ITC;
            }
        }

        // Increment the array index
        nUsed++;

Continue:
        // Find "<postfix>"
        if (!FindRspPostfix(szRsp, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        // Skip over additional empty line in EHS5.
        if (UNNECESSARY_BLANK_LINES_IN_RESPONSE)
            (void)FindRspPostfix(szRsp, szRsp);
    }

#ifdef RIL_ENABLE_ALS
    // Workaround: the Microsoft MMI doesn't display the line number of incoming calls unless
    // these two lines are in the own number directory. If they are missing we create faked
    // entries to let the MMI display at least the line number.
    for ( ; dwCurrentAddressId <= 2; dwCurrentAddressId++)
    {
        char szNr[2];

        if (nUsed == nAllocated) {
            if (!AllocateOrReallocateStorage((BYTE**)&rgrsi, sizeof(RILSUBSCRIBERINFO), nUsed, &nAllocated, NETWK_ALLOC_NUMBER)) {
                hr = E_OUTOFMEMORY;
                goto Error;
            }
        }

        memset(&rgrsi[nUsed], 0x00, sizeof(RILSUBSCRIBERINFO));
        rgrsi[nUsed].cbSize = sizeof(RILSUBSCRIBERINFO);

        rgrsi[nUsed].dwAddressId = dwCurrentAddressId++;
        rgrsi[nUsed].dwParams |= RIL_PARAM_SI_ADDRESSID;

        // We have to provide a telephone number. Since this number this number might be shown
        // we fill in the ALS (Cinterion style) line number instead of a random one.
        szNr[0] = '0' + (char)dwCurrentAddressId;
        szNr[1] = '\0';
        (void)StringToRILAddress(szNr, (BYTE)161, rgrsi[nUsed].raAddress);
        rgrsi[nUsed].dwParams |= RIL_PARAM_SI_ADDRESS;
        // Increment the array index
        nUsed++;
    }
#endif
    pBlob = (void*)rgrsi;
    cbBlob = nUsed * sizeof(RILSUBSCRIBERINFO);

Error:
    if (FAILED(hr)) {
        (void)FreeBlob(rgrsi);
    }
    return hr;
}

static HRESULT ParseGetSubscriberNumbersCDMA(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    char szMSID[MAX_ATCMD_LEN];
    RILSUBSCRIBERINFO* rgrsi = NULL;
    HRESULT hr = E_FAIL;

    pBlob = NULL;
    cbBlob = 0;

    memset(szMSID, 0, MAX_ATCMD_LEN);

    rgrsi = (RILSUBSCRIBERINFO *)AllocBlob(sizeof(RILSUBSCRIBERINFO));
    if (rgrsi == NULL) goto Error;

    if (!ParseRspPrefix(szRsp, szRsp) ||
        !MatchAltStringBeginning(szRsp, "$MDN: ", "$MSID: ", szRsp, NULL) ||
        !ParseUnquotedString(szRsp, '\r', szMSID, MAX_ATCMD_LEN, szRsp)) {
        goto Error;
    }

    // Use default value, if MDN/MSID isn't programmed yet.
    if (strlen(szMSID) == 0)
        (void)strncpyz(szMSID, "0000000000", MAX_ATCMD_LEN);

    memset(rgrsi, 0x00, sizeof(RILSUBSCRIBERINFO));
    rgrsi->cbSize = sizeof(RILSUBSCRIBERINFO);

    (void)StringToRILAddress(szMSID, (BYTE)161, rgrsi->raAddress);
    rgrsi->dwParams |= RIL_PARAM_SI_ADDRESS;

    pBlob = (void*)rgrsi;
    cbBlob = sizeof(RILSUBSCRIBERINFO);
    hr = S_OK;
Error:
    if (FAILED(hr)) {
        (void)FreeBlob(rgrsi);
    }
    return hr;
}

//
//
//
HRESULT RILDrv_GetSubscriberNumbers(DWORD dwParam)
{
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);

    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (g_PowerManager.IsRadioOff() || (g_dwReadyState & (RIL_READYSTATE_UNLOCKED))  != (RIL_READYSTATE_UNLOCKED))
    {
        // As long the own number phonebook is not ready, we should not block a multipexer channel with this
        // command. Instead we notify the OS, when the own numbers are accesible.
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETSUBSCRIBERNUMBERS, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)(g_PowerManager.IsRadioOff() ? RIL_E_RADIOOFF : RIL_E_SIMBUSY)))
            hr = E_FAIL;
        goto Error;
    }

    if (pHandle->GetDevice()->GetCurrentTechnology() == TEC_MODE_CDMA) {
        // Debug modules only: the MDN might not be available. In this case use the
        // MSID instead, since Windows Mobile insists on a value.
        if (!QueueCmdWithTimeout(HCCMD_STANDARD, pHandle, USE_MDN_READ_CMD?"AT$MDN?\r":"AT$MDN\r", USE_MDN_READ_CMD?"AT$MSID?\r":"AT$MSID\r", CMDOPT_PLAN_B, APIID_GETSUBSCRIBERNUMBERS, ParseGetSubscriberNumbersCDMA, NULL, hr, 0, 0, 0)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else {
        if (!QueueCmdWithRetry(HCCMD_STANDARD, pHandle, "AT+CNUM\r", CMDOPT_NONE, APIID_GETSUBSCRIBERNUMBERS, ParseGetSubscriberNumbers, NULL, hr, 8, 6000)) {
            hr = E_FAIL;
            goto Error;
        }
    }

Error:
    return hr;
}

//
//
//
HRESULT ParseGetCurrentRadioBand(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    char szTemp[MAX_ATCMD_LEN];

    pBlob = NULL;
    cbBlob = 0;

    if (!ParseRspPrefix(szRsp, szRsp) ||
        !MatchStringBeginning(szRsp, "^SCFG: \"Radio/Band\"", szRsp)) {
        goto Error;
    }
    if (!MatchStringBeginning(szRsp, ",", szRsp)  ||
        !ParseString(szRsp, szTemp, MAX_ATCMD_LEN, szRsp)) {
        goto Error;
    }
    g_dwLastKnowBandSelection = atoi(szTemp);

Error:
    // We don't care about errors since we have a good default value
    return S_OK;
}

//
//
//
static HRESULT ParseGetCurrentRadioBandGroup(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    char szTemp[MAX_ATCMD_LEN];
    DWORD *pdwGroup;

    pBlob = NULL;
    cbBlob = 0;
    pdwGroup = (DWORD *)pParam;

    if (!ParseRspPrefix(szRsp, szRsp) ||
        !MatchStringBeginning(szRsp, "^SCFG: \"Radio/Band/Group\"", szRsp)) {
        goto Error;
    }
    if (!MatchStringBeginning(szRsp, ",", szRsp)  ||
        !ParseString(szRsp, szTemp, MAX_ATCMD_LEN, szRsp)) {
        goto Error;
    }
    if (pdwGroup != NULL)
        *pdwGroup = atoi(szTemp);

Error:
    // We don't care about errors since we have a good default value
    return S_OK;
}

//
//
//
static HRESULT ParseGetOperatorList(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, UINT nOPListCmd, CRilHandle *pRILDevice)
{
    UINT nValue;
    UINT nUsed = 0;
    UINT nAllocated = 0;
    RILOPERATORINFO* rgroi = NULL;
    RILOPERATORINFO oTemp;
    UINT uiEonsType = 5;
    HRESULT hr = S_OK;
    const char *pszPrefix;

    pBlob = NULL;
    cbBlob = 0;

    switch (nOPListCmd) {
        case OPLIST_SOPS:
            pszPrefix = "^SOPS: ";
            break;
        case OPLIST_SNWS:
            pszPrefix = "^SNWS: ";
            break;
#ifdef SUPPORTS_EHSX_R1
        case OPLIST_XEONS:
            pszPrefix = "+XEONS: ";
            break;
#endif
        case OPLIST_SXEONS:
            pszPrefix = "^SXEONS: ";
            break;
        default:
            pszPrefix = "+COPS: ";
            break;
    }

    // Parse "<prefix>+COPS: " (or "^SOPS", "^SNWS", "+XEONS:")
    if (!ParseRspPrefix(szRsp, szRsp) ||
        !MatchStringBeginning(szRsp, pszPrefix, szRsp)) {
        // If we don't get anything back, just return S_OK with pBlob=NULL and cbBlob=0.
        //        hr = E_FAIL;
        goto Error;
    }

    // Parse "("
    while (MatchStringBeginning(szRsp, "(", szRsp)) {
#ifndef RIL_WM6_ACT_SELECTION
        BOOL bFound;
        UINT i;
#endif

        memset(&oTemp, 0x00, sizeof(RILOPERATORINFO));
        oTemp.cbSize = sizeof(RILOPERATORINFO);
        oTemp.ronNames.cbSize = sizeof(RILOPERATORNAMES);

        // Parse "<stat>"
        if (!ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            goto Continue;
        }
        if (NUM_OPERSTATS > nValue) {
            oTemp.dwStatus = g_rgdwOperStats[nValue];
        } else {
            oTemp.dwStatus = RIL_OPSTATUS_UNKNOWN;
        }
        oTemp.dwParams |= RIL_PARAM_OI_STATUS;

        if (nOPListCmd == OPLIST_SOPS) {
            // Parse "<eonsType>" (used for operator name selection, if EONS is enabled)
            if (!MatchStringBeginning(szRsp, ",", szRsp)  || !ParseUInt(szRsp, TRUE, nValue, szRsp)) {
                goto Continue;
            }
            uiEonsType = nValue;
        }

        // Parse ",<long_name>"
        if (UCS2_NETWORK_NAMES) {
            WCHAR wszName[MAXLENGTH_OPERATOR_LONG];

            if (!MatchStringBeginning(szRsp, ",", szRsp)  ||
                !ParseUCS2String(szRsp, wszName, MAXLENGTH_OPERATOR_LONG, szRsp)) {
                goto Continue;
            }
            (void)_snprintfz(oTemp.ronNames.szLongName, MAXLENGTH_OPERATOR_LONG, "%S", wszName);
        }
        else {
            if (!MatchStringBeginning(szRsp, ",", szRsp)  ||
                !ParseString(szRsp, oTemp.ronNames.szLongName, MAXLENGTH_OPERATOR_LONG, szRsp)) {
                goto Continue;
            }
        }

        if (*oTemp.ronNames.szLongName) {
            oTemp.ronNames.dwParams |= RIL_PARAM_ON_LONGNAME;
            oTemp.dwParams |= RIL_PARAM_OI_NAMES;
        }

        // Parse ",<short_name>" (or service provider name acording to EONS specification, if AT^SOPS=? is used)
        if (MatchStringBeginning(szRsp, ",", szRsp))  {
            char szOpName[MAXLENGTH_OPERATOR_LONG];

            memset(oTemp.ronNames.szShortName, 0, MAXLENGTH_OPERATOR_SHORT);
            if (UCS2_NETWORK_NAMES) {
                WCHAR wszName[MAXLENGTH_OPERATOR_LONG];

                if (ParseUCS2String(szRsp, wszName, MAXLENGTH_OPERATOR_LONG, szRsp)) {
                    (void)_snprintfz(szOpName, MAXLENGTH_OPERATOR_LONG, "%S", wszName);
                    if (oTemp.dwStatus == RIL_OPSTATUS_CURRENT) {    // The service provider field is only valid for current operator.
                        (void)strncpyz(oTemp.ronNames.szShortName, szOpName, MAXLENGTH_OPERATOR_SHORT);
                        if (*oTemp.ronNames.szShortName) {
                            oTemp.ronNames.dwParams |= RIL_PARAM_ON_SHORTNAME;
                            oTemp.dwParams |= RIL_PARAM_OI_NAMES;
                        }
                    }
                }
            }
            else {
                if (ParseString(szRsp, szOpName, MAXLENGTH_OPERATOR_LONG, szRsp)) {
                    if (oTemp.dwStatus == RIL_OPSTATUS_CURRENT) {    // The service provider field is only valid for current operator.
                        (void)strncpyz(oTemp.ronNames.szShortName, szOpName, MAXLENGTH_OPERATOR_SHORT);
                        if (*oTemp.ronNames.szShortName) {
                            oTemp.ronNames.dwParams |= RIL_PARAM_ON_SHORTNAME;
                            oTemp.dwParams |= RIL_PARAM_OI_NAMES;
                        }
                    }
                }
            }
        }

        if (nOPListCmd == OPLIST_SOPS) {
            // Parse "<ServiceProviderType>" (ignored)
            if (!MatchStringBeginning(szRsp, ",", szRsp)  || !ParseUInt(szRsp, TRUE, nValue, szRsp)) {
                goto Continue;
            }
        }

        // Parse ",<num_name>)"
        if (UCS2_NETWORK_NAMES) {
            WCHAR wszName[MAXLENGTH_OPERATOR_NUMERIC];

            if (!MatchStringBeginning(szRsp, ",", szRsp)  ||
                !ParseUCS2String(szRsp, wszName, MAXLENGTH_OPERATOR_NUMERIC, szRsp)) {
                goto Continue;
            }
            (void)_snprintfz(oTemp.ronNames.szNumName, MAXLENGTH_OPERATOR_NUMERIC, "%S", wszName);
        }
        else {
            if (!MatchStringBeginning(szRsp, ",", szRsp)  ||
                !ParseString(szRsp, oTemp.ronNames.szNumName, MAXLENGTH_OPERATOR_NUMERIC, szRsp)) {
                goto Continue;
            }
        }
        if (*oTemp.ronNames.szNumName) {
            oTemp.ronNames.dwParams |= RIL_PARAM_ON_NUMNAME;
            oTemp.dwParams |= RIL_PARAM_OI_NAMES;
        }

        // Parse ",<AcT>"
        if (!MatchStringBeginning(szRsp, ",", szRsp)               ||
            !ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            goto Continue;
        }
        // Parse ")" (not for AT^SXEONS, skip over possible optional parameters)
#ifdef SUPPORTS_EHSX_R1
        if (nOPListCmd != OPLIST_SXEONS && nOPListCmd != OPLIST_XEONS)
#else
        if (nOPListCmd != OPLIST_SXEONS)
#endif
            (void)MatchStringAnywhere(szRsp, ")", szRsp);

#ifdef RIL_WM6_ACT_SELECTION
        switch (nValue)
        {
        case 0:
            oTemp.ronNames.dwParams |= RIL_PARAM_ON_GSM_ACT;
            break;

        case 1:
            oTemp.ronNames.dwParams |= RIL_PARAM_ON_GSMCOMPACT_ACT;
            break;

        case 2:
            oTemp.ronNames.dwParams |= RIL_PARAM_ON_UMTS_ACT;
            break;

        case 7:
            oTemp.ronNames.dwParams |= RIL_PARAM_ON_LTE_ACT;
            break;
        default:
            break;
        }
#endif

#ifdef SUPPORTS_EHSX_R1
        if (nOPListCmd == OPLIST_SXEONS || nOPListCmd == OPLIST_XEONS) {
#else
        if (nOPListCmd == OPLIST_SXEONS) {
#endif
            WCHAR wszName[MAXLENGTH_OPERATOR_LONG];

            // Parse ",<EONS_long>"
            if (!MatchStringBeginning(szRsp, ",", szRsp)  ||
                !ParseUCS2String(szRsp, wszName, MAXLENGTH_OPERATOR_LONG, szRsp)) {
                goto Continue;
            }
            if (wcslen(wszName) > 0) {
                // If EONS name is given, use it and set EONS type to 1.
                (void)_snprintfz(oTemp.ronNames.szLongName, MAXLENGTH_OPERATOR_LONG, "%S", wszName);
                uiEonsType = 1;
            }

            // Parse ",<EONS_short>"
            if (!MatchStringBeginning(szRsp, ",", szRsp)  ||
                !ParseUCS2String(szRsp, wszName, MAXLENGTH_OPERATOR_LONG, szRsp)) {
                goto Continue;
            }
            if (wcslen(wszName) > 0) {
                char szOpName[MAXLENGTH_OPERATOR_LONG];

                (void)_snprintfz(szOpName, MAXLENGTH_OPERATOR_LONG, "%S", wszName);
                if (oTemp.dwStatus == RIL_OPSTATUS_CURRENT) {    // The service provider field is only valid for current operator.
                    (void)strncpyz(oTemp.ronNames.szShortName, szOpName, MAXLENGTH_OPERATOR_SHORT);
                    if (*oTemp.ronNames.szShortName) {
                        oTemp.ronNames.dwParams |= RIL_PARAM_ON_SHORTNAME;
                        oTemp.dwParams |= RIL_PARAM_OI_NAMES;
                    }
                }
            }
            // Parse ")"
            (void)MatchStringBeginning(szRsp, ")", szRsp);
        }

        if (oTemp.dwStatus == RIL_OPSTATUS_CURRENT && *oTemp.ronNames.szShortName && nOPListCmd == OPLIST_SOPS && pRILDevice->GetEons()->UseServiceProvider() == USE_SERVICE_PROVDER_NAME_ON) {
            // Special case: serive provider name is available and active. In this case the
            // service provider name can be found a short name and must be used as
            // long name (type: EONS).
            (void)strncpyz(oTemp.ronNames.szLongName, oTemp.ronNames.szShortName, MAXLENGTH_OPERATOR_LONG);
            uiEonsType = 1;
        }

        // Workaround needed for EONS names.
        if (nOPListCmd == OPLIST_SOPS && uiEonsType == 1 && MUST_CHECK_SIM_FOR_EONS) {
            (void)pRILDevice->GetEons()->GetOperatorInfoFromTable((WORD)atoi(oTemp.ronNames.szNumName), oTemp.ronNames.szLongName, NULL, MAXLENGTH_OPERATOR_LONG);
        }

        // Fill in the long operator name, if we didn't get it
        GetLongOperatorName(&oTemp.ronNames, (nOPListCmd == OPLIST_SOPS && uiEonsType < 4 && *oTemp.ronNames.szLongName)?TRUE:FALSE);

        // According to the user 2G / 3G selection we don't show those networks which are using the wrong technology.
        if ((g_n2G3GSelection == SELECTION2G3G_UNKNOWN) || (g_n2G3GSelection == SELECTION2G3G_AUTOMATIC) || (g_n2G3GSelection == SELECTION2G3G_GSM_ONLY && nValue == 0) || (g_n2G3GSelection == SELECTION2G3G_UMTS_ONLY && nValue == 2) || (g_n2G3GSelection == SELECTION2G3G_LTE_ONLY && nValue == 7))
        {
#ifndef RIL_WM6_ACT_SELECTION
            // Test for duplicated entries (operators which have a 2G and a 3G network), if 2G/3G feature from WM6 is not used.
            bFound = FALSE;
            for (i=0; i<nUsed && !bFound; i++) {
                if ((oTemp.dwParams & RIL_PARAM_OI_NAMES) && (rgroi[i].dwParams & RIL_PARAM_OI_NAMES)) {
                    if ((oTemp.ronNames.dwParams & RIL_PARAM_ON_NUMNAME) && (rgroi[i].ronNames.dwParams & RIL_PARAM_ON_NUMNAME)) {
                        if (strcmp(rgroi[i].ronNames.szNumName, oTemp.ronNames.szNumName) == 0)
                            bFound = TRUE;
                    }
                }
            }
            if (!bFound) {
#endif
                if (nUsed == nAllocated) {
                    if (!AllocateOrReallocateStorage((BYTE**)&rgroi, sizeof(RILOPERATORINFO), nUsed, &nAllocated, NETWK_ALLOC_NUMBER)) {
                        hr = E_OUTOFMEMORY;
                        goto Error;
                    }
                }
                memcpy(&rgroi[nUsed], &oTemp, sizeof(RILOPERATORINFO));

                // Increment the array index
                nUsed++;
#ifndef RIL_WM6_ACT_SELECTION
            }
#endif
        }

Continue:
        // Parse ","
        if (!MatchStringAnywhere(szRsp, ",", szRsp)) {
            break;
        }
    }

    // NOTE: there may be more data here, but we don't care about it

    // Find "<postfix>"
    if (!FindRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    pBlob = (void*)rgroi;
    cbBlob = nUsed * sizeof(RILOPERATORINFO);

    // Old operator list in memory? Delete it now.
    if (g_pOperatorListBlob != NULL)
        FreeBlob(g_pOperatorListBlob);
    g_pOperatorListBlob = NULL;
    // Make a copy from the new operator list.
    g_cbOperatorListBlob = cbBlob;
    if (cbBlob > 0) {
        g_pOperatorListBlob = (BYTE *)AllocBlob(cbBlob);
        if (g_pOperatorListBlob == NULL)
            g_cbOperatorListBlob = 0;
        else
            memcpy(g_pOperatorListBlob, pBlob, cbBlob);
    }

Error:
    if (FAILED(hr)) {
        (void)FreeBlob(rgroi);
    }
    return hr;
}


static HRESULT ParseGetOperatorListCOPS(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    return ParseGetOperatorList(szRsp, pBlob, cbBlob, OPLIST_COPS, (CRilHandle *)pParam);
}

static HRESULT ParseGetOperatorListSOPS(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    return ParseGetOperatorList(szRsp, pBlob, cbBlob, OPLIST_SOPS, (CRilHandle *)pParam);
}

static HRESULT ParseGetOperatorListSNWS(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    return ParseGetOperatorList(szRsp, pBlob, cbBlob, OPLIST_SNWS, (CRilHandle *)pParam);
}

static HRESULT ParseGetOperatorListStored(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    BYTE* pBuffer = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    pBuffer = (BYTE*)AllocBlob(g_cbOperatorListBlob);
    if (!pBuffer) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    memcpy(pBuffer, g_pOperatorListBlob, g_cbOperatorListBlob);
    pBlob = pBuffer;
    cbBlob = g_cbOperatorListBlob;

Error:
    if (FAILED(hr)) {
        (void)FreeBlob(pBlob);
    }
    return hr;
}

#ifdef SUPPORTS_EHSX_R1
static HRESULT ParseGetOperatorListXEONS(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    return ParseGetOperatorList(szRsp, pBlob, cbBlob, OPLIST_XEONS, (CRilHandle *)pParam);
}
#endif

static HRESULT ParseGetOperatorListIntel(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    return ParseGetOperatorList(szRsp, pBlob, cbBlob, OPLIST_SXEONS, (CRilHandle *)pParam);
}

//
//
//
HRESULT RILDrv_GetOperatorList(DWORD dwParam)
{
    bool fDeactivateDataConnection = false;
    HRESULT hr = S_OK;
    CCommand *pCmd = NULL;
    UINT nBands = 0;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (USE_GETOPERATORS_ONLY_WHEN_SIM_READY && (g_dwReadyState & (RIL_READYSTATE_SIM|RIL_READYSTATE_UNLOCKED|RIL_READYSTATE_SMS)) != (RIL_READYSTATE_SIM|RIL_READYSTATE_UNLOCKED|RIL_READYSTATE_SMS)) {
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETOPERATORLIST, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_E_NETWKTEMPFAILURE)) {
            hr = E_FAIL;
        }
        goto Error;
    }

    if (AVOID_COPS_ACT) {
        // AT+COPS/<Act> bug in PXS8: as a workaround for the not functioning <AcT>
        // parameter the RIL configures the Radio/Band to select the access technology.
        // When searching for networks
        if (QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(), "AT^SCFG=Radio/Band\r", CMDOPT_NONE, APIID_SCFGRADIOBAND, 0, 0, 0, ParseGet2G3GSelectionSCFG, NULL, (LPVOID)&nBands)) {
            if (nBands != 7679 && nBands != 0)
                (void)QueueInternalCmd(HCCMD_STANDARD, pHandle->GetDevice(), "AT^SCFG=Radio/Band,\"7679\",\"1\"\r", CMDOPT_NONE, APIID_SCFGRADIOBAND, 0, 0, 0, 0, NULL);
        }
    }

    // Get information, if there is currently a command running on the command port.
    pCmd = pHandle->GetDevice()->GetCurrentCommand();
    if (pCmd != NULL)
    {
        // Is a search for the current operator list currently ongoing?
        if (pCmd->GetAPIID() == APIID_GETOPERATORLIST)
        {
            // Since there is already a search for the current operators on its way, the RIL
            // doesn't start a new search. Instead it will wait of its result and return it.
            if (!QueueCmdWithRetry(HCCMD_STANDARD, pHandle, "AT\r", CMDOPT_NONE, APIID_GETOPERATORLIST, ParseGetOperatorListStored, NULL, hr, 0, 0, NULL, (LPVOID)pHandle->GetDevice())) {
                hr = E_FAIL;
            }
            goto Error;
        }
    }

    if (GetRegistryBoolean(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("EndDataConnectionForNetworkSearch"), &fDeactivateDataConnection) && fDeactivateDataConnection) {
        // The HC25 Rel2 is not able to perform a network search, if any data connectetion
        // is active. If the registry key is active, the RIL ends the all active data connections
        // before performing the network search.
        if (pHandle->GetDevice()->GetComDevice(DATA_PORT)->FDataCsdMode())
            (void)QueueInternalCmd(HCCMD_STANDARD, pHandle->GetDevice(), "ATH\r", CMDOPT_NONE, APIID_SETGPRSATTACHED, 0, 0, 0, NULL);
        (void)QueueInternalCmd(HCCMD_STANDARD, pHandle->GetDevice(), "AT+CGATT=0\r", CMDOPT_NONE, APIID_SETGPRSATTACHED, 0, 0, 0, NULL);
    }

    if (g_bNeedAllBandNetworkSelection && g_dwAutomaticGroupSearchTimeout > 0)
    {
        char szCmd[MAX_ATCMD_LEN];

        g_dwLastKnowBandSelection = 127;
        if (NEED_RESET_BAND_SETTINGS_AFTER_SNWS && g_dwAutomaticGroupSearchTimeout == 0)
            (void)QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(), "AT^SCFG=\"Radio/Band\"\r", CMDOPT_NONE, APIID_SCFGRADIOBAND, 0, 0, 0, ParseGetCurrentRadioBand);

        // The 'All Band Network Selection' uses the AT^SNWS=? command
        if (!QueueCmdWithRetry(HCCMD_STANDARD, pHandle, "AT^SNWS=?\r", CMDOPT_NONE, APIID_GETOPERATORLIST, ParseGetOperatorListSNWS, NULL, hr, 2, 7500, NULL, (LPVOID)pHandle->GetDevice())) {
            hr = E_FAIL;
            goto Error;
        }
        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SCFG=\"Radio/Band\",\"%d\"\r", g_dwLastKnowBandSelection);

        if (NEED_RESET_BAND_SETTINGS_AFTER_SNWS && g_dwAutomaticGroupSearchTimeout == 0)
            (void)QueueInternalCmd(HCCMD_STANDARD, pHandle->GetDevice(), szCmd, CMDOPT_NONE, APIID_SCFGRADIOBAND, 0, 0, 0);
    }
    else
    {
        DWORD dwForceCOPS = 0;

        (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("NoEONSforOperatorList"), &dwForceCOPS);
        if (pHandle->GetDevice()->GetEons()->GetEONSState() == EONS_ENABLED && dwForceCOPS == 0)
        {
            const char *szNetCmd = "AT^SOPS=?\r";
            PFN_CMD_PARSE fnParsing = ParseGetOperatorListSOPS;

            // If we have native EONS support, we can take adventage of the enhance operator selection command.
            if (INTEL_STYLE_NET_CMDS) {
                szNetCmd = "AT^SXEONS\r";
                fnParsing = ParseGetOperatorListIntel;
            }
#ifdef SUPPORTS_EHSX_R1
            if (INTEL_AT_CMDS) {
                szNetCmd = "AT+XEONS\r";
                fnParsing = ParseGetOperatorListXEONS;
            }
#endif

            if (!QueueCmdWithRetry(HCCMD_STANDARD, pHandle, szNetCmd, CMDOPT_NONE, APIID_GETOPERATORLIST, fnParsing, NULL, hr, 2, 7500, NULL, (LPVOID)pHandle->GetDevice())) {
                hr = E_FAIL;
                goto Error;
            }
        }
        else
        {
            // If necessary, put radio in special no-EONS-mode.
            if (SPECIAL_COPS_MODE_FOR_NO_EONS)
                (void)QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(), "AT+COPS=3,90\r", CMDOPT_NONE, APIID_SETGPRSATTACHED, 0, 0, 0, NULL);

            if (!QueueCmdWithRetry(HCCMD_STANDARD, pHandle, "AT+COPS=?\r", CMDOPT_NONE, APIID_GETOPERATORLIST, ParseGetOperatorListCOPS, NULL, hr, 2, 7500, NULL, (LPVOID)pHandle->GetDevice())) {
                hr = E_FAIL;
                goto Error;
            }
        }
    }

    if (nBands != 7679 && nBands != 0) {
        // Switch back to the original band configuration.
        char szCmd[MAX_ATCMD_LEN];

        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SCFG=\"Radio/Band\",\"%d\",\"1\"\r", nBands);
        (void)QueueInternalCmd(HCCMD_STANDARD, pHandle->GetDevice(), szCmd, CMDOPT_NONE, APIID_SCFGRADIOBAND, 0, 0, 0, NULL);
    }

Error:
    return hr;
}


static DWORD g_dwNextFreeOperatorIndex = 1;

//
//
//
static HRESULT ParseGetPreferredOperatorList(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    UINT nValue;
    UINT nUsed = 0;
    UINT nAllocated = 0;
    UINT nAcT1, nAcT2, nAcT3;
    RILOPERATORINFO oiTemp;
    RILOPERATORINFO* rgroi = NULL;
    HRESULT hr = S_OK;
    char rgszTemp[20];
    UINT iBufferPos;

    pBlob = NULL;
    cbBlob = 0;

    EnterCriticalSection(&g_PPLMNBufferSection);

    if (pParam == NULL) {
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<prefix>"
    if (!ParseRspPrefix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    g_iPPLMNBufferPos = PPLMN_BUFFER_NOT_FILLED;
    for (iBufferPos = 0; iBufferPos < MAX_PPLMN_BUFFER; iBufferPos++) {
        g_PPLMNBuffer[iBufferPos] = 0;
        g_PPLMNBufferParam[iBufferPos] = 0;
    }
    iBufferPos = 0;

    // Parse "+CPOL: "
    while (MatchStringBeginning(szRsp, "+CPOL: ", szRsp)) {
        // We first create a temporary entry and decide later if we keep it.
        memset(&oiTemp, 0x00, sizeof(RILOPERATORINFO));

        oiTemp.cbSize = sizeof(RILOPERATORINFO);
        oiTemp.ronNames.cbSize = sizeof(RILOPERATORNAMES);

        // Parse "<index>"
        if (!ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            goto Continue;
        }
        oiTemp.dwIndex = nValue;
        oiTemp.dwParams |= RIL_PARAM_OI_INDEX;

        // Parse ","
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            goto Continue;
        }
        // Parse "<format>"
        if (!ParseUInt(szRsp, TRUE, nValue, szRsp)) {
            goto Continue;
        }

        // Parse ","
        if (!MatchStringBeginning(szRsp, ",", szRsp)) {
            goto Continue;
        }
        // The name came in numeric format
        // Parse "<operator_name>"
        if (!ParseString(szRsp, rgszTemp, sizeof(rgszTemp), szRsp)) {
            goto Continue;
        }
        nValue = atoi(rgszTemp);
        (void)_snprintfz(oiTemp.ronNames.szNumName, MAXLENGTH_OPERATOR_NUMERIC, "%u", nValue);
        oiTemp.ronNames.dwParams |= RIL_PARAM_ON_NUMNAME;
        oiTemp.dwParams |= RIL_PARAM_OI_NAMES;

        if (oiTemp.dwIndex < MAX_PPLMN_BUFFER) {
            g_PPLMNBuffer[oiTemp.dwIndex] = nValue;
            g_PPLMNBufferParam[oiTemp.dwIndex] = 0;
        }

        // Fill in the long operator name, if we didn't get it
        GetLongOperatorName(&oiTemp.ronNames, FALSE);

        nAcT1 = nAcT2 = nAcT3 = 0;
        if (MatchStringBeginning(szRsp, ",", szRsp)) {
            // Parse "<GSM_AcT>,<GSM_Compact_AcT>,<UTRAN_AcT>" (optional parameters only on special SIMs)
            if (!ParseUInt(szRsp, TRUE, nAcT1, szRsp)    ||
                !MatchStringBeginning(szRsp, ",", szRsp) ||
                !ParseUInt(szRsp, TRUE, nAcT2, szRsp)    ||
                !MatchStringBeginning(szRsp, ",", szRsp) ||
                !ParseUInt(szRsp, TRUE, nAcT3, szRsp)) {
                goto Continue;
            }
#ifdef RIL_WM6_ACT_SELECTION
            if (1 == nAcT3) {
                oiTemp.ronNames.dwParams |= RIL_PARAM_ON_UMTS_ACT;
            }
#ifdef HANDLE_GSMCOMPACT_ACT
            if (1 == nAcT2) {
                oiTemp.ronNames.dwParams |= RIL_PARAM_ON_GSMCOMPACT_ACT;
            }
#endif
            if (1 == nAcT1) {
                oiTemp.ronNames.dwParams |= RIL_PARAM_ON_GSM_ACT;
            }
            if (oiTemp.dwIndex < MAX_PPLMN_BUFFER) {
                g_PPLMNBufferParam[oiTemp.dwIndex] = (oiTemp.ronNames.dwParams & (RIL_PARAM_ON_GSM_ACT|RIL_PARAM_ON_GSMCOMPACT_ACT|RIL_PARAM_ON_UMTS_ACT));
            }
#else
#if defined(RIL_PARAM_ON_UMTS_ACT)
            g_PPLMNBufferParam[oiTemp.dwIndex] = (RIL_PARAM_ON_UMTS_ACT|RIL_PARAM_ON_GSM_ACT);
#else
            g_PPLMNBufferParam[oiTemp.dwIndex] = 1;
#endif
#endif
        }
        // Sometimes an invalid PPLMN entry shows up in the list which usually appears as
        // '0000' or 'ffff'. We don't report this kind of entry to the OS.
        if (nValue != 0) {
            if (nUsed == nAllocated) {
                if (!AllocateOrReallocateStorage((BYTE**)&rgroi, sizeof(RILOPERATORINFO), nUsed, &nAllocated, NETWK_ALLOC_NUMBER)) {
                    hr = E_OUTOFMEMORY;
                    goto Error;
                }
            }
            memcpy(&rgroi[nUsed], &oiTemp, sizeof(RILOPERATORINFO));
#ifdef RIL_WM6_ACT_SELECTION
            if (g_PPLMNBufferParam[oiTemp.dwIndex] == 0) {
                rgroi[nUsed].ronNames.dwParams |= RIL_PARAM_ON_GSM_ACT;
            }
            rgroi[nUsed].dwIndex = (2 * (rgroi[nUsed].dwIndex - 1)) + 1;
#endif
            if (oiTemp.dwIndex > iBufferPos) {
                iBufferPos = oiTemp.dwIndex;
            }
            // Increment the array index
            nUsed++;
#ifdef RIL_WM6_ACT_SELECTION
            if (g_PPLMNBufferParam[oiTemp.dwIndex] != 0) {
                if (g_PPLMNBufferParam[oiTemp.dwIndex] == (RIL_PARAM_ON_GSM_ACT|RIL_PARAM_ON_UMTS_ACT)) {
                    rgroi[nUsed-1].ronNames.dwParams &= ~RIL_PARAM_ON_UMTS_ACT;
                    if (nUsed == nAllocated) {
                        if (!AllocateOrReallocateStorage((BYTE**)&rgroi, sizeof(RILOPERATORINFO), nUsed, &nAllocated, NETWK_ALLOC_NUMBER)) {
                            hr = E_OUTOFMEMORY;
                            goto Error;
                        }
                    }
                    memcpy(&rgroi[nUsed], &oiTemp, sizeof(RILOPERATORINFO));
                    rgroi[nUsed].ronNames.dwParams |= RIL_PARAM_ON_UMTS_ACT;
                    rgroi[nUsed].ronNames.dwParams &= ~RIL_PARAM_ON_GSM_ACT;
                    rgroi[nUsed].dwIndex = (2 * (rgroi[nUsed].dwIndex - 1)) + 2;
                    nUsed++;
                }
            }
            else {
                if (nUsed == nAllocated) {
                    if (!AllocateOrReallocateStorage((BYTE**)&rgroi, sizeof(RILOPERATORINFO), nUsed, &nAllocated, NETWK_ALLOC_NUMBER)) {
                        hr = E_OUTOFMEMORY;
                        goto Error;
                    }
                }
                memcpy(&rgroi[nUsed], &oiTemp, sizeof(RILOPERATORINFO));
                rgroi[nUsed].ronNames.dwParams |= RIL_PARAM_ON_UMTS_ACT;
                rgroi[nUsed].dwIndex = (2 * (rgroi[nUsed].dwIndex - 1)) + 2;
                nUsed++;
            }
#endif
        }

Continue:
        // Find "<postfix>"
        if (!FindRspPostfix(szRsp, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        // Skip over additional empty line in EHS5.
        if (UNNECESSARY_BLANK_LINES_IN_RESPONSE)
            (void)FindRspPostfix(szRsp, szRsp);
    }

    if (iBufferPos < MAX_PPLMN_BUFFER) {
        g_iPPLMNBufferPos = iBufferPos;
    }
    pBlob = (void*)rgroi;
    cbBlob = nUsed * sizeof(RILOPERATORINFO);

Error:
    LeaveCriticalSection(&g_PPLMNBufferSection);
    if (FAILED(hr)) {
        (void)FreeBlob(rgroi);
    }
    return hr;
}

//
//
//
static HRESULT ParseGetPreferredOperatorListUnavailable(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/, DWORD& dwCode)
{
    LPCSTR szDummy;

    // If there is no response string, the radio is currently off.
    if (szRsp == NULL)
        return RIL_E_RADIOOFF;

    // '+CME ERROR: 3' indicates, that there is no user PPLMN available. In order
    // not to block the Microsoft MMI, we must return an empty list in this case.
    if (MatchStringAnywhere(szRsp, "+CME ERROR: 3\r", szDummy) || (WRONG_SIM_FAILURE_AT_CPOL && MatchStringAnywhere(szRsp, "+CME ERROR: 13\r", szDummy)))
    {
        // Since this isn't an error, we reconfigure this response to be an 'OK' instead.
        if (pBlob != NULL)
            (void)FreeBlob(pBlob);
        pBlob = NULL;
        cbBlob = 0;
        dwCode = RIL_RESULT_OK;
    }
    return S_OK;
}

//
//
//
HRESULT RILDrv_GetPreferredOperatorList(DWORD dwParam, DWORD dwFormat)
{
    UINT i;
    char *pszCmd;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);

    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    // Figure out the operator formats ID to use
    for (i = 0; i < NUM_OPERFORMATS; i++) {
        if (dwFormat == g_rgdwOperFormats[i]) {
            break;
        }
    }

    if (NUM_OPERFORMATS == i) {
        hr = E_INVALIDARG;
        goto Error;
    }

    // The AT+CPOL command might need to be configured to the numeriv format.
    if (NEED_CONFIG_CPOL)
        pszCmd = "AT+CPOL=,2;+CPOL?\r";
    else
        pszCmd = "AT+CPOL?\r";

    EnterCriticalSection(&g_PPLMNBufferSection);
    g_dwNextFreeOperatorIndex = 1;
    g_iPPLMNBufferPos = PPLMN_BUFFER_NOT_FILLED;
    LeaveCriticalSection(&g_PPLMNBufferSection);

    if (!QueueCmdWithRetry(HCCMD_STANDARD, pHandle, pszCmd, CMDOPT_NONE, APIID_GETPREFERREDOPERATORLIST, ParseGetPreferredOperatorList, NULL, hr, 2, 5000, ParseGetPreferredOperatorListUnavailable, (LPVOID)pHandle->GetDevice())) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

// Problem: the module returns a "SIM failure" in case the SIM is actually
// only busy.
static const CME_TRANSLATION s_CMET_CPOL_SIMFAIL[] = {{RIL_E_SIMFAILURE, RIL_E_SIMBUSY},{NULL,NULL}};

//
//
//
HRESULT RILDrv_AddPreferredOperator(DWORD dwParam, DWORD dwIndex, const RILOPERATORNAMES* lpOperatorNames)
{
    char szCmd[NETWK_CMDBUF_LENGTH];
    LPSTR szWalk = szCmd;
    char szCmdB[NETWK_CMDBUF_LENGTH];
    LPSTR szWalkB = szCmdB;
    HRESULT hr = S_OK;
    UINT i;
    UINT iIndex = 0;
    UINT iOperator = 0;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    const CME_TRANSLATION *pCMET = NULL;

    EnterCriticalSection(&g_PPLMNBufferSection);

    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    // Add the index (if supplied)
    if (RIL_PREFOPINDEX_FIRSTAVAILABLE == dwIndex) {
        if (g_iPPLMNBufferPos == PPLMN_BUFFER_NOT_FILLED)
        {
            iIndex = g_dwNextFreeOperatorIndex;
            g_dwNextFreeOperatorIndex++;
        }
        else
        {
            for (iIndex = 1; iIndex < MAX_PPLMN_BUFFER; iIndex++)
            {
                if (g_PPLMNBuffer[iIndex] == 0)
                    break;
            }
        }

        (void)_snprintfz(szWalk, NETWK_CMDBUF_LENGTH - (szWalk - szCmd), "AT+CPOL=%u,2,", iIndex);
        (void)_snprintfz(szWalkB, NETWK_CMDBUF_LENGTH - (szWalkB - szCmdB), "AT+CPOL=%u,2,", iIndex);
    } else {
        iIndex = dwIndex;
#ifdef RIL_WM6_ACT_SELECTION
        iIndex = ((iIndex + 1) / 2);
#endif
        (void)_snprintfz(szWalk, NETWK_CMDBUF_LENGTH - (szWalk - szCmd), "AT+CPOL=%u,2,", iIndex);
        (void)_snprintfz(szWalkB, NETWK_CMDBUF_LENGTH - (szWalkB - szCmdB), "AT+CPOL=%u,2,", iIndex);
    }
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    szWalkB = strchr(szWalkB, '\0');  // NO_TYPO: 27

    if (lpOperatorNames->dwParams & RIL_PARAM_ON_NUMNAME) {
        // We have a numeric name -- use it
        (void)_snprintfz(szWalk, NETWK_CMDBUF_LENGTH - (szWalk - szCmd), "\"%s\"", lpOperatorNames->szNumName);
        (void)_snprintfz(szWalkB, NETWK_CMDBUF_LENGTH - (szWalkB - szCmdB), "\"%s\"", lpOperatorNames->szNumName);
        iOperator = atoi(lpOperatorNames->szNumName);
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        szWalkB = strchr(szWalkB, '\0');  // NO_TYPO: 27
    }
    else {
        // The HC25 only supports the numeric name, so in all other cases, the RIL must return with an error.
        hr = E_INVALIDARG;
        goto Error;
    }
#ifdef RIL_WM6_ACT_SELECTION
    // Select access type, if SIM supports this (Plan B: try it without access type).
    // Combinations with the RIL_PARAM_ON_GSMCOMPACT_ACT are not supported, because the HC25 is not using them.
    if ((lpOperatorNames->dwParams & (RIL_PARAM_ON_UMTS_ACT|RIL_PARAM_ON_GSM_ACT)) == (RIL_PARAM_ON_UMTS_ACT|RIL_PARAM_ON_GSM_ACT)) {
        (void)strncpyz(szWalk, ",1,0,1", NETWK_CMDBUF_LENGTH - (szWalk - szCmd));
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    }
    else if (lpOperatorNames->dwParams & RIL_PARAM_ON_UMTS_ACT) {
#ifdef FORCE_UMTS_PLUS_GSM
        (void)strncpyz(szWalk, ",1,0,1", NETWK_CMDBUF_LENGTH - (szWalk - szCmd));
#else
        (void)strncpyz(szWalk, ",0,0,1", NETWK_CMDBUF_LENGTH - (szWalk - szCmd));
#endif
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    }
    else if (lpOperatorNames->dwParams & RIL_PARAM_ON_GSMCOMPACT_ACT) {
        (void)strncpyz(szWalk, ",0,1,0", NETWK_CMDBUF_LENGTH - (szWalk - szCmd));
        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
    }
    else {
        if (lpOperatorNames->dwParams & RIL_PARAM_ON_GSM_ACT) {
#ifdef FORCE_UMTS_PLUS_GSM
            (void)strncpyz(szWalk, ",1,0,1", NETWK_CMDBUF_LENGTH - (szWalk - szCmd));
#else
            (void)strncpyz(szWalk, ",1,0,0", NETWK_CMDBUF_LENGTH - (szWalk - szCmd));
#endif
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        }
        else {
            // No parameters? Use GSM+UMTS as default
            (void)strncpyz(szWalk, ",1,0,1", NETWK_CMDBUF_LENGTH - (szWalk - szCmd));
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        }
    }
#else
    // If the USIM PPLMN parameters are availabe, they are mandatory.
    // Plan B: do it the old way without these parameters.
    (void)strncpyz(szWalk, ",1,0,1", NETWK_CMDBUF_LENGTH - (szWalk - szCmd));
    szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
#endif
    (void)strncpyz(szWalk, "\r", NETWK_CMDBUF_LENGTH - (szWalk - szCmd));  // NO_TYPO: 30
    (void)strncpyz(szWalkB, "\r", NETWK_CMDBUF_LENGTH - (szWalkB - szCmdB));  // NO_TYPO: 30

#ifdef RIL_WM6_ACT_SELECTION
    // Since every operator can be more than one in the list, for each network type once, this check is not needed, if PPLMN list supports network types.
    if (iIndex < MAX_PPLMN_BUFFER && g_iPPLMNBufferPos != PPLMN_BUFFER_NOT_FILLED && iOperator != 0)
#else
    if (iIndex < MAX_PPLMN_BUFFER && g_iPPLMNBufferPos != PPLMN_BUFFER_NOT_FILLED && iOperator != 0)
#endif
    {
        // Since the HC25 doesn't allow the same operator twice in the list,
        // we now have to recheck the whole command:

        // Step 1: Is the same operator in the list before our position? If yes, we just do nothing.
        for (i = 1; i < iIndex; i++)
        {
            if (g_PPLMNBuffer[i] == iOperator)
                break;
        }
        if (i < iIndex)
        {
            (void)_snprintfz(szCmd, NETWK_CMDBUF_LENGTH, "AT+CPOL=%u;+CPOL=%u,2,\"%u\",1,0,1\r", i, i, iOperator);
            (void)_snprintfz(szCmdB, NETWK_CMDBUF_LENGTH, "AT+CPOL=%u,2,\"%u\"\r", i, iOperator);
#if defined(RIL_PARAM_ON_UMTS_ACT)
            g_PPLMNBufferParam[i] = (RIL_PARAM_ON_GSM_ACT|RIL_PARAM_ON_UMTS_ACT);
#else
            g_PPLMNBufferParam[i] = 1;
#endif
        }
        else
        {
            // Step 2: Is the same operator in the list behind our position? If so, we have to delete it first.

            for (i = iIndex+1; i < MAX_PPLMN_BUFFER; i++)
            {
                if (g_PPLMNBuffer[i] == iOperator)
                    break;
            }

            if (i < MAX_PPLMN_BUFFER)
            {
                (void)_snprintfz(szCmd, NETWK_CMDBUF_LENGTH, "AT+CPOL=%u;+CPOL=%u,2,\"%u\",1,0,1\r", i, iIndex, iOperator);
                (void)_snprintfz(szCmdB, NETWK_CMDBUF_LENGTH, "AT+CPOL=%u;+CPOL=%u,2,\"%u\"\r", i, iIndex, iOperator);
                g_PPLMNBuffer[i] = 0;
                g_PPLMNBufferParam[i] = 0;
            }
            if (iIndex < MAX_PPLMN_BUFFER && i < MAX_PPLMN_BUFFER)
            {
                g_PPLMNBuffer[iIndex] = iOperator;
#if defined(RIL_PARAM_ON_UMTS_ACT)
                g_PPLMNBufferParam[i] = (RIL_PARAM_ON_GSM_ACT|RIL_PARAM_ON_UMTS_ACT);
#else
                g_PPLMNBufferParam[i] = 1;
#endif
            }
        }
    }

    if (WRONG_SIM_FAILURE_AT_CPOL)
        pCMET = s_CMET_CPOL_SIMFAIL;
    if (strcmp(szCmd, szCmdB) == 0)
    {
        if (!QueueCmdWithTimeout(HCCMD_STANDARD, pHandle, szCmd, NULL, CMDOPT_NONE, APIID_ADDPREFERREDOPERATOR, NULL, NULL, hr, 0, 0, 0, NULL, NULL, pCMET)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else{
        if (!QueueCmdWithTimeout(HCCMD_STANDARD, pHandle, szCmd, szCmdB, CMDOPT_PLAN_B, APIID_ADDPREFERREDOPERATOR, NULL, NULL, hr, 0, 0, 0, NULL, NULL, pCMET)) {
            hr = E_FAIL;
            goto Error;
        }
    }

Error:
    LeaveCriticalSection(&g_PPLMNBufferSection);
    return hr;
}

#ifdef RIL_WM6_ACT_SELECTION
static HRESULT ParseErrorDeleteEntries(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/, DWORD& dwCode)
{
    LPCSTR szDummy;

    // If there is no response string, the radio is currently off.
    if (szRsp == NULL)
        return RIL_E_RADIOOFF;

    // '+CME ERROR: 21' isn't concidered as an error.
    if (MatchStringAnywhere(szRsp, "+CME ERROR: 21\r", szDummy))
    {
        // Since this isn't an error, we reconfigure this responnse to be an 'OK' instead.
        if (pBlob != NULL)
            (void)FreeBlob(pBlob);
        pBlob = NULL;
        cbBlob = 0;
        dwCode = RIL_RESULT_OK;
    }
    return S_OK;
}
#endif



//
//
//
HRESULT RILDrv_RemovePreferredOperator(DWORD dwParam, DWORD dwIndex)
{
    char szCmd[MAX_ATCMD_LEN];
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    DWORD dwIndexVirt;
    PFN_CMD_ERROR pfErrorHandler = NULL;
    const CME_TRANSLATION *pCMET = NULL;

    EnterCriticalSection(&g_PPLMNBufferSection);

    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    dwIndexVirt = dwIndex;
#ifdef RIL_WM6_ACT_SELECTION
    dwIndexVirt = ((dwIndexVirt + 1) / 2);
    pfErrorHandler = ParseErrorDeleteEntries;
#endif

    // Add the index
    (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+CPOL=%u\r", dwIndexVirt);

    if (dwIndex < MAX_PPLMN_BUFFER) {
        g_PPLMNBuffer[dwIndex] = 0;
        g_PPLMNBufferParam[dwIndex] = 0;
    }
    if (WRONG_SIM_FAILURE_AT_CPOL)
        pCMET = s_CMET_CPOL_SIMFAIL;
    if (!QueueCmdWithTimeout(HCCMD_STANDARD, pHandle, szCmd, NULL, CMDOPT_NONE, APIID_REMOVEPREFERREDOPERATOR, NULL, NULL, hr, 0, 0, 0, pfErrorHandler, NULL, pCMET)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    LeaveCriticalSection(&g_PPLMNBufferSection);
    return hr;
}

//
//
//
DWORD WINAPI PerformBandGroupSearchThread(LPVOID pVoid)
{
    CRilHandle* pHandle = (CRilHandle *)pVoid;
    DWORD dwUnregisteredSince = 0;
    DWORD dwNextGroup = 0;
    DWORD dwTimeInterval = 0;
    DWORD dwWaitAfterGroup2 = 0;

    (void)QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle, "AT^SCFG=\"Radio/Band/Group\"\r", CMDOPT_NONE, APIID_SCFGRADIOBAND, 0, 0, 0, ParseGetCurrentRadioBandGroup, NULL, (LPVOID)&dwNextGroup);
    if (dwNextGroup == 0)        // If radio band group is "0", the band group search starts with 1, otherwise with 0.
        dwNextGroup = 1;
    else
        dwNextGroup = 0;

    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("AutomaticGroupSearchInterval"), &dwTimeInterval);
    if (dwTimeInterval == 0)
        dwTimeInterval = 250;
    (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("AutomaticGroupSearchWait2"), &dwWaitAfterGroup2);
    if (dwWaitAfterGroup2 == 0)
        dwWaitAfterGroup2 = 5000;

    // This thread cancels, if radio is off, RIL shuts down or the module is manually de-registered.
    while(!pHandle->FCancelSet() && !g_PowerManager.IsRadioOff() && !pHandle->NeedBandGroupSearchActivation() && g_iLastCOPS != 2)
    {
        if (g_dwRegStatus == RIL_REGSTAT_HOME || g_dwRegStatus == RIL_REGSTAT_ROAMING)
        {
            if (dwUnregisteredSince > 0)
            {
                // Set back to automatic band search mode for next time.
                (void)QueueInternalCmd(HCCMD_STANDARD, pHandle, "AT^SCFG=\"Radio/Band/Group\",\"2\"\r", CMDOPT_NONE, APIID_SCFGRADIOBAND, 0, 0, 0);
                // After applying this command, the module briefly de-registers and registers again.
                // To prevent this thread to start again with the band group switching, it will be set
                // on held for this time.
                Sleep(dwWaitAfterGroup2);

                if (RADIOBANDGROUP_OVERWRITES_ACT && (g_n2G3GSelection == SELECTION2G3G_GSM_ONLY || g_n2G3GSelection == SELECTION2G3G_UMTS_ONLY))
                {
                    // Problem: using the radio band group selection sets the GSM-only / UMTS-only
                    // selection back to automatic. Therfore this setting must be newly applied.
                    if (QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle, "AT+COPS=3,2;+COPS?\r", CMDOPT_NONE, APIID_GETCURRENTOPERATOR, 0, 0, 0, ParseGetCurrentOperator))
                    {
                        char szCmd[MAX_ATCMD_LEN];

                        if (g_iLastCOPS == 0)
                        {
                            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+COPS=0,,,%d\r", g_n2G3GSelection == SELECTION2G3G_GSM_ONLY?0:2);
                            (void)QueueInternalCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_NONE, APIID_REGISTERONNETWORK, 0, 0, 0);
                        }
                        if (g_iLastCOPS == 1 && g_iLastCOPSOpr > 0)
                        {
                            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+COPS=1,2,%u,%d\r", g_iLastCOPSOpr, g_n2G3GSelection == SELECTION2G3G_GSM_ONLY?0:2);
                            (void)QueueInternalCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_NONE, APIID_SCFGRADIOBAND, 0, 0, 0);
                        }

                    }
                }
            }
            dwUnregisteredSince = 0;
        }
        else
        {
            if (dwUnregisteredSince == 0)
                dwUnregisteredSince = GetTickCount();
        }

        if (dwUnregisteredSince != 0 && (GetTickCount() - dwUnregisteredSince) > g_dwAutomaticGroupSearchTimeout)
        {
            char szCmd[NETWK_CMDBUF_LENGTH];

            dwUnregisteredSince = 0;
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SCFG=\"Radio/Band/Group\",\"%d\"\r", dwNextGroup);
            if (dwNextGroup == 0)
                dwNextGroup = 1;
            else
                dwNextGroup = 0;
            (void)QueueInternalCmd(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_NONE, APIID_SCFGRADIOBAND, 0, 0, 0);
        }

        // Unfortunately there aren't events for every cancel / action criteria,
        // so this functions polls every second.
        Sleep(dwTimeInterval);
    }
    return 0;
}

//
//
//
HRESULT ParseGetCurrentOperator(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    UINT nValue;
    UINT nFormat;
    RILOPERATORNAMES* pron = NULL;
    HRESULT hr = S_OK;
    BOOL bHaveEONSName = FALSE;

    pBlob = NULL;
    cbBlob = 0;

    pron = (RILOPERATORNAMES*)AllocBlob(sizeof(RILOPERATORNAMES));
    if (!pron) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pron, 0x00, sizeof(RILOPERATORNAMES));
    pron->cbSize = sizeof(RILOPERATORNAMES);

    // Parse "<prefix>+COPS: <mode>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+COPS: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    if (WRONG_COPS_VALUE_AFTER_SNWS && g_iLastCOPS == 1 && nValue == 0 && g_bNeedAllBandNetworkSelection == TRUE)
        nValue = g_iLastCOPS;
    else
        g_iLastCOPS = nValue;

    // See if we have the operator info
    if (!MatchStringBeginning(szRsp, ",", szRsp)) {
        // We're not currently registered with an operator
        hr = RIL_E_OPFMTUNAVAILABLE;
        goto Error;
    }

    // Parse "<format>"
    if (!ParseUInt(szRsp, TRUE, nFormat, szRsp) ||
        !MatchStringBeginning(szRsp, ",", szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    if (!nFormat && !UCS2_NETWORK_NAMES) {
        // The name came in long format
        // Parse "<operator_name>"
        if(!ParseString(szRsp, pron->szLongName, MAXLENGTH_OPERATOR_LONG, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (*pron->szLongName) {
            pron->dwParams |= RIL_PARAM_ON_LONGNAME;
        }
    } else if (1 == nFormat && !UCS2_NETWORK_NAMES) {
        // The name came in short format
        // Parse "<operator_name>"
        if(!ParseString(szRsp, pron->szShortName, MAXLENGTH_OPERATOR_SHORT, szRsp)) {
            hr = E_FAIL;
            goto Error;
        }
        if (*pron->szShortName) {
            pron->dwParams |= RIL_PARAM_ON_SHORTNAME;
        }
    } else if (2 == nFormat) {
        // The name came in numeric format
        // Parse "<operator_name>"
        if (UCS2_NETWORK_NAMES) {
            WCHAR wszShortName[MAXLENGTH_OPERATOR_SHORT];

            if(!ParseUCS2String(szRsp, wszShortName, MAXLENGTH_OPERATOR_SHORT, szRsp)) {
                hr = E_FAIL;
                goto Error;
            }
            nValue = _wtoi(wszShortName);
        }
        else if (SPACE_IN_NUMERIC_NETWORK_NAME) {
            char szShortName[MAXLENGTH_OPERATOR_SHORT];

            if(!ParseString(szRsp, szShortName, MAXLENGTH_OPERATOR_SHORT, szRsp)) {
                hr = E_FAIL;
                goto Error;
            }
            // There may be one unexpected space in position 3. If so, delete it.
            if (szShortName[3] == ' ')
                memmove(&szShortName[3], &szShortName[4], MAXLENGTH_OPERATOR_SHORT-4);
            nValue = atoi(szShortName);
        }
        else {
            if (*szRsp=='\"') szRsp++;    // Ignore " characters around operator number

            if (!ParseUInt(szRsp, TRUE, nValue, szRsp)) {
                hr = E_FAIL;
                goto Error;
            }

            if (*szRsp=='\"') szRsp++;    // Ignore " characters around operator number
        }

        g_iLastCOPSOpr = nValue;
        (void)_snprintfz(pron->szNumName, MAXLENGTH_OPERATOR_NUMERIC, "%u", nValue);
        pron->dwParams |= RIL_PARAM_ON_NUMNAME;
    } else {
        // Unknown name format
        hr = E_FAIL;
        goto Error;
    }

    // Parse "<AcT>"
    if (MatchStringBeginning(szRsp, ",", szRsp)) {
        UINT nAcT;

        if (ParseUInt(szRsp, TRUE, nAcT, szRsp)) {
            UINT *pAcT;

            pAcT = (UINT*)pParam;
            if (NEW_COPS_ACT_VALUES && nAcT > 2) {
                // The module gives the <AcT> values according to 3GPP 27.007 Rel8. These
                // values must be translated to the old values from Rel7.
                switch (nAcT) {
                    case 3:             // GSM/E-GPRS
                        // Handle like <AcT> = 0
                        nAcT = 0;
                        break;
                    case 4:             // UTRAN/HSDPA
                    case 5:             // UTRAN/HSUPA
                    case 6:             // UTRAN/HSDPA+HSUPA
                        // Handle like <AcT> = 2
                        nAcT = 2;
                        break;
                    case 7:             // LTE
                        nAcT = 7;
                        break;
                    default:
                        // Unhandled value, handle as if <AcT> is not given.
                        pAcT = NULL;
                }
            }

#ifdef RIL_WM6_ACT_SELECTION
            if (nAcT == 0)
                pron->dwParams |= RIL_PARAM_ON_GSM_ACT;
            else if (nAcT == 2)
                pron->dwParams |= RIL_PARAM_ON_UMTS_ACT;
            else if (nAcT == 7)
                pron->dwParams |= RIL_PARAM_ON_LTE_ACT;
#endif
            if (pAcT != NULL)
                *pAcT = nAcT;
        }
    }

    // Parse "<postfix>"
    if (!ParseRspPostfix(szRsp, szRsp)) {
        hr = E_FAIL;
        goto Error;
    }

    // If we know THE EONS names, we use them instead of any possible returen values of AT+COPS
    if (strlen(g_szLastKnownEONSNameLong) > 0) {
        (void)strncpyz(pron->szLongName, g_szLastKnownEONSNameLong, MAXLENGTH_OPERATOR_LONG);
        pron->dwParams |= RIL_PARAM_ON_LONGNAME;
        bHaveEONSName = TRUE;
    }
    if (strlen(g_szLastKnownEONSNameShort) > 0) {
        (void)strncpyz(pron->szShortName, g_szLastKnownEONSNameShort, MAXLENGTH_OPERATOR_SHORT);
        pron->dwParams |= RIL_PARAM_ON_SHORTNAME;
        bHaveEONSName = TRUE;
        if (strlen(g_szLastKnownEONSNameLong) == 0) {
            (void)strncpyz(pron->szLongName, g_szLastKnownEONSNameShort, MAXLENGTH_OPERATOR_LONG);
            pron->dwParams |= RIL_PARAM_ON_LONGNAME;
        }
    }

    // Fill in the long operator name, if we didn't get it.
    GetLongOperatorName(pron, bHaveEONSName);

    pBlob = (void*)pron;
    cbBlob = sizeof(RILOPERATORNAMES);

Error:
    if (FAILED(hr)) {
        (void)FreeBlob(pron);
    }
    return hr;
}

//
//
//
static HRESULT ParseNoCurrentOperator(LPCSTR /*szRsp*/, void*& /*pBlob*/, UINT& /*cbBlob*/, LPVOID /*pParam*/)
{
    // This is the appropiate error code, if there we are not registered to the network.
    return (g_PowerManager.IsRadioOff()? RIL_E_RADIOOFF : RIL_E_OPFMTUNAVAILABLE);
}

//
//
//
static HRESULT ParseCdmaCurrentOperator(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    RILOPERATORNAMES* pron = NULL;
    HRESULT hr = S_OK;
    WCHAR wszNetworkName[MAX_ATCMD_LEN];
    WCHAR wszNumName[MAX_ATCMD_LEN];

    memset(wszNetworkName, 0, sizeof(wszNetworkName));
    memset(wszNumName, 0, sizeof(wszNumName));

    // If there is a command response available, try to
    if (szRsp != NULL) {
        UINT uiDummy;
        UINT uiMCC, uiMNC;

        if (ParseRspPrefix(szRsp, szRsp) &&
            MatchStringBeginning(szRsp, "^SMONI: ", szRsp) &&
            ParseUInt(szRsp, TRUE, uiDummy, szRsp) &&
            MatchStringBeginning(szRsp, ",", szRsp) &&
            ParseUInt(szRsp, TRUE, uiDummy, szRsp) &&
            MatchStringBeginning(szRsp, ",", szRsp) &&
            ParseUInt(szRsp, TRUE, uiDummy, szRsp) &&
            MatchStringBeginning(szRsp, ",", szRsp) &&
            ParseUInt(szRsp, TRUE, uiMCC, szRsp) &&
            MatchStringBeginning(szRsp, ",", szRsp) &&
            ParseUInt(szRsp, TRUE, uiMNC, szRsp)
          ) {
            // Generate numeric network name from MCC and MNC,
            (void)_snwprintfz(wszNumName, MAX_ATCMD_LEN, L"%03d%03d", uiMCC, uiMNC);
        }
        // Skip over rest of the response.
        (void)FindRspPostfix(szRsp, szRsp);
    }

    (void)GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("CDMANetworkName"), wszNetworkName, MAX_ATCMD_LEN);
    if (wcslen(wszNumName) == 0)  // Use registry key only, if operator not found in AT response.
        (void)GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("CDMANetworkNumName"), wszNumName, MAX_ATCMD_LEN);
    else  // Update registry key, if operator has been detected.
        (void)SetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("CDMANetworkNumName"), wszNumName);

    pBlob = NULL;
    cbBlob = 0;

    pron = (RILOPERATORNAMES*)AllocBlob(sizeof(RILOPERATORNAMES));
    if (!pron) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pron, 0x00, sizeof(RILOPERATORNAMES));
    pron->cbSize = sizeof(RILOPERATORNAMES);

    g_iLastCOPS = 0;

    pron->dwParams = RIL_PARAM_ON_LONGNAME|RIL_PARAM_ON_SHORTNAME|RIL_PARAM_ON_NUMNAME;
    (void)_snprintfz(pron->szLongName, MAXLENGTH_OPERATOR_LONG, "%S", wszNetworkName);
    (void)_snprintfz(pron->szShortName, MAXLENGTH_OPERATOR_SHORT, "%S", wszNetworkName);
    (void)_snprintfz(pron->szNumName, MAXLENGTH_OPERATOR_NUMERIC, "%S", wszNumName);

    if (wcslen(wszNetworkName) == 0) {
        // Try to find operator in the list of the GSM operators, if no name is
        // available in the registry key.
        pron->dwParams &= ~RIL_PARAM_ON_LONGNAME;
        GetLongOperatorName(pron, FALSE);
        (void)strncpyz(pron->szShortName, pron->szLongName, MAXLENGTH_OPERATOR_SHORT);
    }

    pBlob = (void*)pron;
    cbBlob = sizeof(RILOPERATORNAMES);

Error:
    if (FAILED(hr)) {
        (void)FreeBlob(pron);
    }
    return hr;
}

//
//
//
HRESULT RILDrv_GetCurrentOperator(DWORD dwParam, DWORD dwFormat)
{
    char szCmd[MAX_ATCMD_LEN];
    UINT i;
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);

    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }
    // Figure out the operator formats ID to use
    for (i = 0; i < NUM_OPERFORMATS; i++) {
        if (dwFormat == g_rgdwOperFormats[i]) {
            break;
        }
    }
    if (NUM_OPERFORMATS == i) {
        hr = E_INVALIDARG;
        goto Error;
    }

    memset(g_szLastKnownEONSNameLong, 0, sizeof(g_szLastKnownEONSNameLong));
    memset(g_szLastKnownEONSNameShort, 0, sizeof(g_szLastKnownEONSNameShort));
    if ((g_dwRegStatus == RIL_REGSTAT_HOME || g_dwRegStatus == RIL_REGSTAT_ROAMING) &&  pHandle->GetDevice()->GetCurrentTechnology() == TEC_MODE_CDMA) {
        DWORD dwCmdOption = CMDOPT_NOOP;
        const char *cszCmd = NULL;

        // If the module supports AT^SMONI for CDMA, use it to get the
        // numeric operator name.
        if (SMONI_REPPORTS_CDMA_MCCMNC) {
            dwCmdOption = CMDOPT_NONE;
            cszCmd = "AT^SMONI\r";
        }
        if (!QueueCmd(HCCMD_STANDARD, pHandle, cszCmd, dwCmdOption, APIID_GETCURRENTOPERATOR, ParseCdmaCurrentOperator, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    // As long we are not registered, the EONS commands might return invalid values.
    // Additionally, the operator should not be queried while the RIL is reading the EONS
    // SIM files.
    else if ((g_dwRegStatus == RIL_REGSTAT_HOME || g_dwRegStatus == RIL_REGSTAT_ROAMING) &&  pHandle->GetDevice()->GetEons()->IsEONSready()) {
        // Check for EONS names using emulated or native support.
        if (!pHandle->GetDevice()->GetEons()->GetOperatorInfo(g_szLastKnownEONSNameLong, g_szLastKnownEONSNameShort, sizeof(g_szLastKnownEONSNameShort))) {
            g_szLastKnownEONSNameLong[0] = 0;
            g_szLastKnownEONSNameShort[0] = 0;
        }

        (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT+COPS=3,%u;+COPS?\r", i);
        if (!QueueCmdWithRetry(HCCMD_STANDARD, pHandle, szCmd, CMDOPT_NONE, APIID_GETCURRENTOPERATOR, ParseGetCurrentOperator, NULL, hr, 2, 1000)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else
    {
        // Some modules block the AT interface, when AT+COPS=3,<format> is used while the module
        // is searching for the network. Since there is anyhow no provider name available in this state
        // we simply return the corresponding error message.
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETCURRENTOPERATOR, ParseNoCurrentOperator, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }

Error:
    return hr;
}

static HRESULT ParseRegisterOnNetworkError(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/, DWORD& dwCode)
{
    LPCSTR szDummy;

    // If there is no response string, the radio is currently off.
    if (szRsp == NULL)
        return RIL_E_RADIOOFF;

    // '+CME ERROR: 30' doesn't indicate a real error, it just means that the module is
    // currently no registered to the network, but is trying to do so.
    if (MatchStringAnywhere(szRsp, "+CME ERROR: 30\r", szDummy))
    {
        // Since this isn't an error, we reconfigure this responnse to be an 'OK' instead.
        if (pBlob != NULL)
            (void)FreeBlob(pBlob);
        pBlob = NULL;
        cbBlob = 0;
        dwCode = RIL_RESULT_OK;
    }
    return S_OK;
}


// A short delay before AT+COPS=1
static HRESULT PreInitCOPS1(CRilHandle * /*pHandle*/, COM_PORT_TYPE /*iPort*/)
{
    Sleep(COPS1_DEFAULT_DELAY);
    return S_OK;
}

//
//
//
HRESULT RILDrv_RegisterOnNetwork(DWORD dwParam, DWORD dwMode, const RILOPERATORNAMES* lpOperatorNames)
{
    HRESULT hr = S_OK;
    char szCmd[NETWK_CMDBUF_LENGTH];
    char szCmdB[NETWK_CMDBUF_LENGTH];
    LPSTR szWalk = szCmd;
    UINT iCOPSMode, iLastCOPS;
    DWORD dwCmdOption = CMDOPT_NONE;
    DWORD dwRetires = 0;
    DWORD dwRetryDelay = 0;
    BOOL bUseQuotes=TRUE;
    BOOL bOmitCOPS = FALSE;
    BOOL bSendCOPStwice = FALSE;
    BOOL bMustResetRadioBand = FALSE;
    BOOL bUnregisterFirst = FALSE;
    const char *pszName;
    UINT nSetRadioBand = 0;
    PFN_CMD_ERROR pErrorHandler = NULL;
    PFN_PRE_CMD_CHECK pPreCmdParse = NULL;

    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    // Figure out the mode ID to use
    for (iCOPSMode = 0; iCOPSMode < NUM_OPSELMODES; iCOPSMode++) {
        if (dwMode == g_rgdwOperSelModes[iCOPSMode]) {
            break;
        }
    }
    if (NUM_OPSELMODES == iCOPSMode) {
        hr = E_INVALIDARG;
        goto Error;
    }
    memset(szCmdB, 0, NETWK_CMDBUF_LENGTH);

    if ((iCOPSMode == 1 || iCOPSMode == 4) && pHandle->GetDevice()->GetCurrentENSSetting() == ENS_MENU_DISABLED) {
        // If manual network selection is currently disabled, generate error message.
        RILRetailTraceZone((ZONE_ATCOMMAND|ZONE_INFO), (TEXT("RilDrv: manual network selection currently disabled\r\n"), iCOPSMode));
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_REGISTERONNETWORK, ParseForceFailureResponse, NULL, hr, NULL, (LPVOID)RIL_E_NETWKTEMPFAILURE)) {
            hr = E_FAIL;
        }
        goto Error;
    }

    // Problem: if we change between two roaming networks, the change will be indicated by a +CREG: 5 URC with
    // new location information. Since the last +CREG-URC was also a +CREG: 5 URC, this would normally not be
    // reported as a regitration state change.
    // Workaround: if we change manually the network, we treat the next +CREG-URC always as an registration state
    // change.
    g_fReportLocationUpdateAsRegistrationState = TRUE;

    // Store last known COPS mode.
    iLastCOPS = g_iLastCOPS;

    hr = S_OK;
    if ((g_PowerManager.IsRadioOff() || (g_dwReadyState & (RIL_READYSTATE_SIM|RIL_READYSTATE_UNLOCKED|RIL_READYSTATE_SMS)) != (RIL_READYSTATE_SIM|RIL_READYSTATE_UNLOCKED|RIL_READYSTATE_SMS)) && (iCOPSMode ==0 || ( lpOperatorNames==NULL ? FALSE : (lpOperatorNames->dwParams & RIL_PARAM_ON_NUMNAME))))
    {
        RILRetailTraceZone((ZONE_ATCOMMAND|ZONE_INFO), (TEXT("RilDrv: AT+COPS=%u scheduled for end of post-PIN initialization\r\n"), iCOPSMode));
        g_iSetCOPSinInit = iCOPSMode;
#ifdef RIL_WM6_ACT_SELECTION
        // If the WM6 style 2G/3G selection is in use, lpOperatorNames->dwParams may contain the
        // selection of 2G or 3G network type. This setting will overwrite the settings from RIL_DevSpecific
        // option RIL_DEVSPECIFIC_SET2G3GSELECTION.
        if (lpOperatorNames!=NULL)
        {
            g_iSetCOPSinInitParams = lpOperatorNames->dwParams;
            if (lpOperatorNames->dwParams & RIL_PARAM_ON_GSM_ACT)
                g_n2G3GSelection = SELECTION2G3G_GSM_ONLY;
            if (lpOperatorNames->dwParams & RIL_PARAM_ON_UMTS_ACT)
                g_n2G3GSelection = SELECTION2G3G_UMTS_ONLY;
            if (lpOperatorNames->dwParams & RIL_PARAM_ON_LTE_ACT)
                g_n2G3GSelection = SELECTION2G3G_LTE_ONLY;
        }
        else
        {
            g_iSetCOPSinInitParams = 0;
            g_n2G3GSelection = SELECTION2G3G_AUTOMATIC;
        }
#endif
        if (iCOPSMode != 0 && lpOperatorNames!=NULL)
        {
            (void)strncpyz(g_szSetCOPSinInitOperator, lpOperatorNames->szNumName, MAXLENGTH_OPERATOR_NUMERIC+1);
            g_szSetCOPSinInitOperator[MAXLENGTH_OPERATOR_NUMERIC] = 0;
        }
        bOmitCOPS = TRUE;
    }
    else
    {
        // Query the current +COPS state for further use. The result will be stored in g_iLastCOPS.
        g_szLastKnownEONSNameLong[0] = 0;
        g_szLastKnownEONSNameShort[0] = 0;
        if (!WRONG_COPS_VALUE_AFTER_SNWS || !pHandle->GetDevice()->NeedBandGroupSearchActivation())
            (void)QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(), "AT+COPS?\r", CMDOPT_NONE, APIID_GETCURRENTOPERATOR, 0, 0, 0, ParseGetCurrentOperator);

        if ((g_dwGPRSRegStatus == RIL_REGSTAT_HOME || g_dwGPRSRegStatus == RIL_REGSTAT_ROAMING) && (g_iLastCOPS != iCOPSMode || iCOPSMode != 0)) {
            bool fGPRSAutoDetach;

            // If we are attached to GPRS, we should detach manually, if configured.
            if (GetRegistryBoolean(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("GPRSAutoDetach"), &fGPRSAutoDetach) && fGPRSAutoDetach) {
                (void)QueueInternalCmd(HCCMD_STANDARD, pHandle->GetDevice(), "AT+CGATT=0\r", CMDOPT_IGNORERSP, APIID_DEVSPECIFIC, 0, 0, 0);
            }
        }

        // If the module is already in auto registration mode it should be avoided to use AT+COPS=0 again.
        if (g_iLastCOPS == iCOPSMode && iCOPSMode == 0)
            bOmitCOPS = TRUE;

        // Workaround: if changing from manual to automatic mode, the AT+COPS=0
        // command should be sent twice.
        if (g_iLastCOPS == 1 && iCOPSMode == 0)
        {
            DWORD dwReg = 0;

            (void)GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("DoubleCOPS0"), &dwReg);
            if (dwReg == 1)
                bSendCOPStwice = TRUE;
        }

        if (NEED_DELAY_ON_COPS1 && iCOPSMode == 1) {
            // The AT+COPS=1 command might not work properly on early versions of the PH8.
            // 1) when there is a time of at least 2 seconds with no AT commands before the AT+COPS=1
            //     command, the command will more likely succeed. This delay is done in the PreInitCOPS1()
            //     function.
            // 2) when there is a problem, the RIL has to abort AT+COPS=1. Afterwards the module is in
            //     an undefinied state. This can only be solved by forcing the module back into the automatic
            //     registration mode. To do this the RIL uses the CMDOPT_PLAN_B command option.
            pPreCmdParse = PreInitCOPS1;
            dwCmdOption |= CMDOPT_PLAN_B;
            dwRetires = 1;
            dwRetryDelay = COPS1_DEFAULT_DELAY;
            (void)strncpyz(szCmdB, "AT+COPS=0\r", NETWK_CMDBUF_LENGTH);
#ifdef RIL_WM6_ACT_SELECTION
            if (g_n2G3GSelection == SELECTION2G3G_GSM_ONLY ||  (lpOperatorNames->dwParams & RIL_PARAM_ON_GSM_ACT))
                (void)strncpyz(szCmdB, "AT+COPS=0,,,0\r", NETWK_CMDBUF_LENGTH);
            else if (g_n2G3GSelection == SELECTION2G3G_UMTS_ONLY || (lpOperatorNames->dwParams & RIL_PARAM_ON_UMTS_ACT))
                (void)strncpyz(szCmdB, "AT+COPS=0,,,2\r", NETWK_CMDBUF_LENGTH);
#else
            if (g_n2G3GSelection == SELECTION2G3G_GSM_ONLY)
                (void)strncpyz(szCmdB, "AT+COPS=0,,,0\r", NETWK_CMDBUF_LENGTH);
            else if (g_n2G3GSelection == SELECTION2G3G_UMTS_ONLY)
                (void)strncpyz(szCmdB, "AT+COPS=0,,,2\r", NETWK_CMDBUF_LENGTH);
#endif
        }

        if (g_iLastCOPS != iCOPSMode || iCOPSMode == 1) {
            g_iLastCOPS = iCOPSMode;
        }
        if (g_iLastCOPS == 1 && lpOperatorNames!=NULL)
            g_iLastCOPSOpr = (UINT)atoi(lpOperatorNames->szNumName);
        else
            g_iLastCOPSOpr = 0;

        // Add the mode
        if (iCOPSMode == 1 && g_bNeedAllBandNetworkSelection && NEED_RESET_BAND_SETTINGS_AFTER_SNWS)  { // 'All Band Network Selection' needed?

            g_dwLastKnowBandSelection = 127;
            if (g_dwAutomaticGroupSearchTimeout == 0)
            {
                (void)QueueInternalCmdBlocking(HCCMD_STANDARD, pHandle->GetDevice(), "AT^SCFG=\"Radio/Band\"\r", CMDOPT_NONE, APIID_SCFGRADIOBAND, 0, 0, 0, ParseGetCurrentRadioBand);
                bMustResetRadioBand = TRUE;
            }

            if (iLastCOPS ==2 && WRONG_COPS_VALUE_AT_STARTUP)
                (void)_snprintfz(szWalk, NETWK_CMDBUF_LENGTH - (szWalk - szCmd), "AT+COPS=0;^SNWS=%u", iCOPSMode);
            else
                (void)_snprintfz(szWalk, NETWK_CMDBUF_LENGTH - (szWalk - szCmd), "AT^SNWS=%u", iCOPSMode);
            pHandle->GetDevice()->SetNeedBandGroupSearchActivation(TRUE);
        }
        else
            (void)_snprintfz(szWalk, NETWK_CMDBUF_LENGTH - (szWalk - szCmd), "AT+COPS=%u", iCOPSMode);

        szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27

        if (iCOPSMode == 1 && NEED_COPS2_BEFORE_COPS1)
            bUnregisterFirst = TRUE;

        if ( (lpOperatorNames!=NULL) && (lpOperatorNames->dwParams!=0) )
        {
            if (lpOperatorNames->dwParams & RIL_PARAM_ON_NUMNAME)
            {
                // numeric format
                (void)strncpyz(szWalk, ",2", NETWK_CMDBUF_LENGTH - (szWalk - szCmd));  // NO_TYPO: 30
                pszName = lpOperatorNames->szNumName;
                bUseQuotes=FALSE;
            }
            else if (lpOperatorNames->dwParams & RIL_PARAM_ON_SHORTNAME)
            {
                // short name format
                (void)strncpyz(szWalk, ",1", NETWK_CMDBUF_LENGTH - (szWalk - szCmd));  // NO_TYPO: 30
                pszName = lpOperatorNames->szShortName;
            }
            else if (lpOperatorNames->dwParams & RIL_PARAM_ON_LONGNAME)
            {
                // long name format
                (void)strncpyz(szWalk, ",0", NETWK_CMDBUF_LENGTH - (szWalk - szCmd));  // NO_TYPO: 30
                pszName = lpOperatorNames->szLongName;
            }
            else
            {
                // None of the names are specified
                hr = E_INVALIDARG;
                goto Error;
            }

            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27

            if ((dwMode!=RIL_OPSELMODE_AUTOMATIC) && (pszName[0]!='\0'))
            {
                if (IGONERE_CMEE_30_ON_COPS)        // Parse error for possible '+CME ERROR: 30', which has to be ignored.
                    pErrorHandler = ParseRegisterOnNetworkError;

                if (COPS_NUM_NAME_IN_UCS2) {
                    WCHAR wszNetworkName[NETWK_CMDBUF_LENGTH];
                    char szEncodedNetworkName[NETWK_CMDBUF_LENGTH];

                    memset(szEncodedNetworkName, 0, NETWK_CMDBUF_LENGTH);
                    (void)_snwprintfz(wszNetworkName, NETWK_CMDBUF_LENGTH, L"%S", pszName);
                    (void)UCS2ToGSMHex(wszNetworkName, wcslen(wszNetworkName), szEncodedNetworkName, NETWK_CMDBUF_LENGTH);
                    (void)_snprintfz(szWalk, NETWK_CMDBUF_LENGTH - (szWalk - szCmd), ",\"%s\"", szEncodedNetworkName);
                }
                else {
                    if (bUseQuotes) {
                        (void)_snprintfz(szWalk, NETWK_CMDBUF_LENGTH - (szWalk - szCmd), ",\"%s\"", pszName);
                    }
                    else {
                        (void)_snprintfz(szWalk, NETWK_CMDBUF_LENGTH - (szWalk - szCmd), ",%s", pszName);
                    }
                }

                szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27

#ifdef RIL_WM6_ACT_SELECTION
                if (AVOID_COPS_ACT) {
                    // AT+COPS/<AcT> bug (PXS8): When using the <AcT> parameter of AT+COPS
                    // the module might freeze. As a workaround, the RIL reconfigures the
                    // AT^SCFG=Radio/Band setting to force the module into the correct
                    // access technology.
                    nSetRadioBand = 7679;    // All GSM/UMTS + CDMA bands.
                    if (g_n2G3GSelection == SELECTION2G3G_GSM_ONLY || (lpOperatorNames->dwParams & RIL_PARAM_ON_GSM_ACT))
                        nSetRadioBand = 7183;    // CDMA + GSM
                    else if (g_n2G3GSelection == SELECTION2G3G_UMTS_ONLY || (lpOperatorNames->dwParams & RIL_PARAM_ON_UMTS_ACT))
                        nSetRadioBand = 7664;    // CDMA + UMTS
                }
                else {
                    if (g_n2G3GSelection == SELECTION2G3G_GSM_ONLY ||  (lpOperatorNames->dwParams & RIL_PARAM_ON_GSM_ACT))
                        (void)strncpyz(szWalk, ",0", NETWK_CMDBUF_LENGTH - (szWalk - szCmd));  // NO_TYPO: 30
                    else if (g_n2G3GSelection == SELECTION2G3G_UMTS_ONLY || (lpOperatorNames->dwParams & RIL_PARAM_ON_UMTS_ACT))
                        (void)strncpyz(szWalk, ",2", NETWK_CMDBUF_LENGTH - (szWalk - szCmd));  // NO_TYPO: 30
                else if (g_n2G3GSelection == SELECTION2G3G_LTE_ONLY || (lpOperatorNames->dwParams & RIL_PARAM_ON_LTE_ACT))
                    (void)strncpyz(szWalk, ",7", NETWK_CMDBUF_LENGTH - (szWalk - szCmd));  // NO_TYPO: 30
                }
#else
                if (AVOID_COPS_ACT) {
                    // AT+COPS/<AcT> bug (PXS8): When using the <AcT> parameter of AT+COPS
                    // the module might freeze. As a workaround, the RIL reconfigures the
                    // AT^SCFG=Radio/Band setting to force the module into the correct
                    // access technology.
                    nSetRadioBand = 7679;    // All GSM/UMTS + CDMA bands.
                    if (g_n2G3GSelection == SELECTION2G3G_GSM_ONLY)
                        nSetRadioBand = 7183;    // CDMA + GSM
                    else if (g_n2G3GSelection == SELECTION2G3G_UMTS_ONLY)
                        nSetRadioBand = 7664;    // CDMA + UMTS
                }
                else {
                    if (g_n2G3GSelection == SELECTION2G3G_GSM_ONLY)
                        (void)strncpyz(szWalk, ",0", NETWK_CMDBUF_LENGTH - (szWalk - szCmd));  // NO_TYPO: 30
                    else if (g_n2G3GSelection == SELECTION2G3G_UMTS_ONLY)
                        (void)strncpyz(szWalk, ",2", NETWK_CMDBUF_LENGTH - (szWalk - szCmd));  // NO_TYPO: 30
                else if (g_n2G3GSelection == SELECTION2G3G_LTE_ONLY)
                    (void)strncpyz(szWalk, ",7", NETWK_CMDBUF_LENGTH - (szWalk - szCmd));  // NO_TYPO: 30
                }
#endif
                szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            }
            else
            {
#ifdef RIL_WM6_ACT_SELECTION
                if (AVOID_COPS_ACT) {
                    // AT+COPS/<AcT> bug (PXS8): When using the <AcT> parameter of AT+COPS
                    // the module might freeze. As a workaround, the RIL reconfigures the
                    // AT^SCFG=Radio/Band setting to force the module into the correct
                    // access technology.
                    nSetRadioBand = 7679;    // All GSM/UMTS + CDMA bands.
                    if (g_n2G3GSelection == SELECTION2G3G_GSM_ONLY ||  (lpOperatorNames->dwParams & RIL_PARAM_ON_GSM_ACT))
                        nSetRadioBand = 7183;    // CDMA + GSM
                    else if (g_n2G3GSelection == SELECTION2G3G_UMTS_ONLY || (lpOperatorNames->dwParams & RIL_PARAM_ON_UMTS_ACT))
                        nSetRadioBand = 7664;    // CDMA + UMTS
                }
                else {
                    if (g_n2G3GSelection == SELECTION2G3G_GSM_ONLY ||  (lpOperatorNames->dwParams & RIL_PARAM_ON_GSM_ACT))
                        (void)strncpyz(szWalk, ",,0", NETWK_CMDBUF_LENGTH - (szWalk - szCmd));  // NO_TYPO: 30
                    else if (g_n2G3GSelection == SELECTION2G3G_UMTS_ONLY || (lpOperatorNames->dwParams & RIL_PARAM_ON_UMTS_ACT))
                        (void)strncpyz(szWalk, ",,2", NETWK_CMDBUF_LENGTH - (szWalk - szCmd));  // NO_TYPO: 30
                else if (g_n2G3GSelection == SELECTION2G3G_LTE_ONLY || (lpOperatorNames->dwParams & RIL_PARAM_ON_LTE_ACT))
                    (void)strncpyz(szWalk, ",,7", NETWK_CMDBUF_LENGTH - (szWalk - szCmd));  // NO_TYPO: 30
                }
#else
                if (AVOID_COPS_ACT) {
                    // AT+COPS/<AcT> bug (PXS8): When using the <AcT> parameter of AT+COPS
                    // the module might freeze. As a workaround, the RIL reconfigures the
                    // AT^SCFG=Radio/Band setting to force the module into the correct
                    // access technology.
                    nSetRadioBand = 7679;    // All GSM/UMTS + CDMA bands.
                    if (g_n2G3GSelection == SELECTION2G3G_GSM_ONLY)
                        nSetRadioBand = 7183;    // CDMA + GSM
                    else if (g_n2G3GSelection == SELECTION2G3G_UMTS_ONLY)
                        nSetRadioBand = 7664;    // CDMA + UMTS
                }
                else {
                    if (g_n2G3GSelection == SELECTION2G3G_GSM_ONLY)
                        (void)strncpyz(szWalk, ",,0", NETWK_CMDBUF_LENGTH - (szWalk - szCmd));  // NO_TYPO: 30
                    else if (g_n2G3GSelection == SELECTION2G3G_UMTS_ONLY)
                        (void)strncpyz(szWalk, ",,2", NETWK_CMDBUF_LENGTH - (szWalk - szCmd));  // NO_TYPO: 30
                else if (g_n2G3GSelection == SELECTION2G3G_LTE_ONLY)
                    (void)strncpyz(szWalk, ",,7", NETWK_CMDBUF_LENGTH - (szWalk - szCmd));  // NO_TYPO: 30
                }
#endif
                szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
            }
        }
        else
        {
            if (AVOID_COPS_ACT) {
                // AT+COPS/<AcT> bug (PXS8): When using the <AcT> parameter of AT+COPS
                // the module might freeze. As a workaround, the RIL reconfigures the
                // AT^SCFG=Radio/Band setting to force the module into the correct
                // access technology.
                nSetRadioBand = 7679;    // All GSM/UMTS + CDMA bands.
                if (g_n2G3GSelection == SELECTION2G3G_GSM_ONLY)
                    nSetRadioBand = 7183;    // CDMA + GSM
                else if (g_n2G3GSelection == SELECTION2G3G_UMTS_ONLY)
                    nSetRadioBand = 7664;    // CDMA + UMTS
            }
            else {
                if (g_n2G3GSelection == SELECTION2G3G_GSM_ONLY)
                    (void)strncpyz(szWalk, ",2,,0", NETWK_CMDBUF_LENGTH - (szWalk - szCmd));  // NO_TYPO: 30
                else if (g_n2G3GSelection == SELECTION2G3G_UMTS_ONLY)
                    (void)strncpyz(szWalk, ",2,,2", NETWK_CMDBUF_LENGTH - (szWalk - szCmd));  // NO_TYPO: 30
            else if (g_n2G3GSelection == SELECTION2G3G_LTE_ONLY)
                (void)strncpyz(szWalk, ",2,,7", NETWK_CMDBUF_LENGTH - (szWalk - szCmd));  // NO_TYPO: 30
            }
            szWalk = strchr(szWalk, '\0');  // NO_TYPO: 27
        }

        (void)strncpyz(szWalk, "\r", NETWK_CMDBUF_LENGTH - (szWalk - szCmd));  // NO_TYPO: 30
    }

    if (bOmitCOPS) {
        // If the AT-command can be omitted (see comments above), we only send a no-operation command.
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_REGISTERONNETWORK, NULL, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else {
        if (bUnregisterFirst) {
            if (NEED_DELAY_ON_COPS2)
                Sleep(COPS2_DEFAULT_DELAY);
            (void)QueueInternalCmd(HCCMD_STANDARD, pHandle->GetDevice(), "AT+COPS=2\r", CMDOPT_NONE, APIID_UNREGISTERFROMNETWORK, 0, 0, 0, NULL);
        }

        if (nSetRadioBand > 0) {
            char szSCFGCmd[MAX_ATCMD_LEN];
            DWORD dwRegRadioBand;

            if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("UseCopsActWorkaround"), &dwRegRadioBand))
                dwRegRadioBand = 1;
            // Set Radio/Band. Needed for AT+COPS/<AcT> workaround.
            (void)_snprintfz(szSCFGCmd, MAX_ATCMD_LEN, "AT^SCFG=\"Radio/Band\",\"%d\",\"1\"\r", nSetRadioBand);
            if (dwRegRadioBand == 1)
                (void)QueueInternalCmd(HCCMD_STANDARD, pHandle->GetDevice(), szSCFGCmd, CMDOPT_NONE, APIID_SCFGRADIOBAND, 0, 0, 0);
        }

        if (bSendCOPStwice)
            (void)QueueInternalCmd(HCCMD_STANDARD, pHandle->GetDevice(), szCmd, CMDOPT_NONE, APIID_REGISTERONNETWORK, 0, 0, 0, NULL, pErrorHandler);

        if (strlen(szCmdB) == 0)             // If no Plan-B command is availabe, use the normal command as default.
            (void)strncpyz(szCmdB, szCmd, NETWK_CMDBUF_LENGTH);
        if (!QueueCmdWithTimeout(HCCMD_STANDARD, pHandle, szCmd, szCmdB, dwCmdOption, APIID_REGISTERONNETWORK, NULL, NULL, hr, 0, dwRetires, dwRetryDelay, pErrorHandler, NULL, NULL, pPreCmdParse)) {
            hr = E_FAIL;
            goto Error;
        }
        if (bMustResetRadioBand) {
            (void)_snprintfz(szCmd, MAX_ATCMD_LEN, "AT^SCFG=\"Radio/Band\",\"%d\"\r", g_dwLastKnowBandSelection);
            (void)QueueInternalCmd(HCCMD_STANDARD, pHandle->GetDevice(), szCmd, CMDOPT_NONE, APIID_SCFGRADIOBAND, 0, 0, 0);
        }
    }

    if (dwMode == RIL_OPSELMODE_AUTOMATIC && pHandle->GetDevice()->NeedBandGroupSearchActivation() && g_dwAutomaticGroupSearchTimeout > 0) {
        pHandle->GetDevice()->SetNeedBandGroupSearchActivation(FALSE);
        CloseHandle(CreateThread(NULL, 0, PerformBandGroupSearchThread, (LPVOID)pHandle->GetDevice(), 0, 0));
    }

    if (COPS_CHANGES_SCFG || (COPS1_CHANGES_SCFG && dwMode == RIL_OPSELMODE_MANUAL)) {
        // Bug in PXS8: the use of AT+COPS changes the module from GSM-only to
        // multimode. If necessary, the RIL must change the setting back.
        if (pHandle->GetDevice()->GetMultiModeSetting() == MULTIMODE_CONFIG_LTE)
            (void)QueueInternalCmd(HCCMD_STANDARD, pHandle->GetDevice(), "AT^SCFG=MEopMode/AccT,\"GSM/UMTS\"\r", CMDOPT_NONE, APIID_SCFGRADIOBAND, 0, 0, 0);
    }

Error:
    return hr;
}


//
//
//
HRESULT RILDrv_UnregisterFromNetwork(DWORD dwParam)
{
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    g_iLastCOPS = 2;

    if (g_PowerManager.IsRadioOff()) {
        RILRetailTraceZone((ZONE_INFO|ZONE_ATCOMMAND), (TEXT("RilDrv: AT+COPS=2 scheduled for post-PIN initialization\r\n")));
        g_iSetCOPSinInit = 2;
#ifdef RIL_WM6_ACT_SELECTION
        g_iSetCOPSinInitParams = 0;
#endif
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_UNREGISTERFROMNETWORK, NULL, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else if (pHandle->GetDevice()->GetCurrentTechnology() == TEC_MODE_CDMA) {
        // There is no special unregister command in CDMA.
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_UNREGISTERFROMNETWORK, NULL, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else {
        if (NEED_DELAY_ON_COPS2)
            Sleep(COPS2_DEFAULT_DELAY);
        if (!QueueCmd(HCCMD_STANDARD, pHandle, "AT+COPS=2\r", CMDOPT_GENERATE_ERROR_CODE, APIID_UNREGISTERFROMNETWORK, NULL, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }

Error:
    return hr;
}


//
//
//
HRESULT ParseGetRegistrationStatus(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    CRilHandle *pDevice;
    UINT nValue;
    DWORD* pdwRegStatus = NULL;
    HRESULT hr = S_OK;
    UINT dwLAC, dwCI, dwAcT = 0xFF;

    pBlob = NULL;
    cbBlob = 0;

    pDevice = (CRilHandle *)pParam;

    pdwRegStatus = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwRegStatus) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pdwRegStatus, 0x00, sizeof(DWORD));

    // Parse "<prefix>+CREG: <mode>,<status>,...<postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CREG: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)         ||
        !MatchStringBeginning(szRsp, ",", szRsp)       ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp))         {
        hr = E_FAIL;
        goto Error;
    }

    dwLAC = 0;
    dwCI = 0;
    // There may be additional location information.
    if (MatchStringBeginning(szRsp, ",\"", szRsp))
    {
        (void)ParseHexUInt(szRsp, TRUE, dwLAC, szRsp);
        (void)MatchStringBeginning(szRsp, "\",\"", szRsp);
        (void)ParseHexUInt(szRsp, TRUE, dwCI, szRsp);
        (void)MatchStringBeginning(szRsp, "\"", szRsp);
        // There may be an additional parameter <AcT>
        if (MatchStringBeginning(szRsp, ",", szRsp)) {
            (void)ParseUInt(szRsp, TRUE, dwAcT, szRsp);
        }
    }

    if (!ParseRspPostfix(szRsp, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    if (NUM_REGSTATS > nValue) {
        *pdwRegStatus = g_rgdwRegStats[nValue];

    if(g_bFakeRegStatusDeniedToHome && (g_rgdwRegStats[nValue] == RIL_REGSTAT_DENIED))
        g_dwGPRSRegStatus = RIL_REGSTAT_HOME;
    } else {
        *pdwRegStatus = RIL_REGSTAT_UNKNOWN;
    }

    g_dwRegStatus = *pdwRegStatus;
    // Special check (needed for +CREG only): any "+CREG: 3" (access denied) is followed by a "+CREG: 0", which is
    // not correct. As a solution the RIL will still report "access denied" until the module has been registered to a
    // network, the SIM is removed or the module has been resetted.
    if (g_dwRegStatus == RIL_REGSTAT_DENIED && CREG0_FOLLOWS_CREG3)
        g_fRegStatus3detected = TRUE;
    if ((g_dwRegStatus == RIL_REGSTAT_UNREGISTERED || g_dwRegStatus == RIL_REGSTAT_ATTEMPTING) && g_fRegStatus3detected)
        g_dwRegStatus = RIL_REGSTAT_DENIED;
    if (g_dwRegStatus == RIL_REGSTAT_HOME || g_dwRegStatus == RIL_REGSTAT_ROAMING)
        g_fRegStatus3detected = FALSE;

    if (pDevice != NULL) {
        if (g_dwRegStatus == RIL_REGSTAT_ROAMING && pDevice->IsNoRoamingIndicator())
            g_dwRegStatus = RIL_REGSTAT_HOME;
        if (g_dwRegStatus == RIL_REGSTAT_ROAMING && g_iLastCOPSOpr != 0 && pDevice->NeedsCheckForNoRoamingNetwork() && pDevice->IsNoRoamingNetwork(g_iLastCOPSOpr))
            g_dwRegStatus = RIL_REGSTAT_HOME;
        *pdwRegStatus = g_dwRegStatus;
        if (pDevice->MustFakeUnregisterdState() && (g_dwRegStatus == RIL_REGSTAT_HOME || g_dwRegStatus == RIL_REGSTAT_ROAMING|| g_dwRegStatus == RIL_REGSTAT_ATTEMPTING))
            *pdwRegStatus = RIL_REGSTAT_UNREGISTERED;
    }

#ifndef RIL_NO_CREG_LOCATION_UPDATES
    if (pDevice != NULL && dwLAC!=0 && dwCI!=0 && (nValue == 1 || nValue == 5))
    {
        CNotificationData *pnd = NULL;
        RILLOCATIONINFO sLI;

        sLI.cbSize = sizeof(RILLOCATIONINFO);
        sLI.dwLocationAreaCode = dwLAC;
        sLI.dwCellID = dwCI;

        pnd = new CNotificationData;
        if (pnd)
        {
            if (!pnd->InitFromRealBlob(RIL_NOTIFY_LOCATIONUPDATE, &sLI, sizeof(RILLOCATIONINFO)))
            {
                delete pnd;
                pnd = NULL;
            }
            pDevice->BroadcastNotification(pnd);
        }
    }
#endif

    pBlob = (void*)pdwRegStatus;
    cbBlob = sizeof(DWORD);

Error:
    if (FAILED(hr)) {
        (void)FreeBlob(pdwRegStatus);
    }
    return hr;
}

//
//
//
static HRESULT ParseGetRegistrationStatusCDMA(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    CRilHandle *pDevice;
    UINT nValue;
    DWORD* pdwRegStatus = NULL;
    HRESULT hr = S_OK;

    pBlob = NULL;
    cbBlob = 0;

    pDevice = (CRilHandle *)pParam;

    pdwRegStatus = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwRegStatus) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pdwRegStatus, 0x00, sizeof(DWORD));

    // Parse "<prefix>^SREG: <mode>,<status><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "^SREG: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp)         ||
        !MatchStringBeginning(szRsp, ",", szRsp)       ||
        !ParseUInt(szRsp, TRUE, nValue, szRsp))         {
        hr = E_FAIL;
        goto Error;
    }

    if (NEW_SREG_PARAM) {
        UINT nValueEVDO;

        // New parameter <statusEVDO>
        if (!MatchStringBeginning(szRsp, ",", szRsp)       ||
            !ParseUInt(szRsp, TRUE, nValueEVDO, szRsp))         {
            hr = E_FAIL;
            goto Error;
        }
        if ((nValueEVDO == 1 || nValueEVDO ==5) && nValue != 1 && nValue != 5)
            nValue = nValueEVDO;
    }

    if (!ParseRspPostfix(szRsp, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    if (NUM_REGSTATS > nValue) {
        *pdwRegStatus = g_rgdwRegStats[nValue];

    if(g_bFakeRegStatusDeniedToHome && (g_rgdwRegStats[nValue] == RIL_REGSTAT_DENIED))
        g_dwGPRSRegStatus = RIL_REGSTAT_HOME;
    } else {
        *pdwRegStatus = RIL_REGSTAT_UNKNOWN;
    }

    g_dwRegStatus = *pdwRegStatus;

    if (pDevice != NULL) {
        if (g_dwRegStatus == RIL_REGSTAT_ROAMING && pDevice->IsNoRoamingIndicator())
            g_dwRegStatus = RIL_REGSTAT_HOME;
        *pdwRegStatus = g_dwRegStatus;
        if (pDevice->MustFakeUnregisterdState() && (g_dwRegStatus == RIL_REGSTAT_HOME || g_dwRegStatus == RIL_REGSTAT_ROAMING|| g_dwRegStatus == RIL_REGSTAT_ATTEMPTING))
            *pdwRegStatus = RIL_REGSTAT_UNREGISTERED;
    }

    pBlob = (void*)pdwRegStatus;
    cbBlob = sizeof(DWORD);

Error:
    if (FAILED(hr)) {
        (void)FreeBlob(pdwRegStatus);
    }
    return hr;
}

//
//
//
static HRESULT ParseGetRegistrationStatusMultiMode(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam)
{
    CRilHandle *pDevice;
    UINT nDummy;
    UINT nValueCREG;
    UINT nValueSREG;
    UINT nValuePSINFO;
    UINT nValue;
    DWORD* pdwRegStatus = NULL;
    HRESULT hr = S_OK;
    UINT dwLAC, dwCI, dwAcT = 0xFF;

    pBlob = NULL;
    cbBlob = 0;

    pDevice = (CRilHandle *)pParam;

    pdwRegStatus = (DWORD*)AllocBlob(sizeof(DWORD));
    if (!pdwRegStatus) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(pdwRegStatus, 0x00, sizeof(DWORD));

    if (!NO_SIND_PSINFO_IN_CDMA) {
        // Parse "<prefix>^SIND: psinfo,<mode>,<status><postfix>"
        if (!ParseRspPrefix(szRsp, szRsp)                  ||
            !MatchStringBeginning(szRsp, "^SIND: psinfo,", szRsp) ||
            !ParseUInt(szRsp, TRUE, nDummy, szRsp)         ||
            !MatchStringBeginning(szRsp, ",", szRsp)       ||
            !ParseUInt(szRsp, TRUE, nValuePSINFO, szRsp))         {
            hr = E_FAIL;
            goto Error;
        }
    }
    else {
        // Default: assume CDMA.
        nValuePSINFO = 11;
    }

    // Parse "<prefix>^SREG: <mode>,<status><postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "^SREG: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nDummy, szRsp)         ||
        !MatchStringBeginning(szRsp, ",", szRsp)       ||
        !ParseUInt(szRsp, TRUE, nValueSREG, szRsp))         {
        hr = E_FAIL;
        goto Error;
    }
    if (NEW_SREG_PARAM) {
        UINT nValueEVDO;

        // New parameter <statusEVDO>
        if (!MatchStringBeginning(szRsp, ",", szRsp)       ||
            !ParseUInt(szRsp, TRUE, nValueEVDO, szRsp))         {
            hr = E_FAIL;
            goto Error;
        }
        if ((nValueEVDO == 1 || nValueEVDO ==5) && nValueSREG != 1 && nValueSREG != 5)
            nValueSREG = nValueEVDO;
    }

    // Parse "<prefix>+CREG: <mode>,<status>,...<postfix>"
    if (!ParseRspPrefix(szRsp, szRsp)                  ||
        !MatchStringBeginning(szRsp, "+CREG: ", szRsp) ||
        !ParseUInt(szRsp, TRUE, nDummy, szRsp)         ||
        !MatchStringBeginning(szRsp, ",", szRsp)       ||
        !ParseUInt(szRsp, TRUE, nValueCREG, szRsp))         {
        hr = E_FAIL;
        goto Error;
    }
    // Bug in PXS8 customer sample: AT+CREG might indicate "unregistered" even
    // though the module is actually registered to UMTS. If this is the case,
    // AT^SIND=psinfo indicates a value between 1 and 10.
    if (CREG0_MIGHT_BE_WRONG && nValueCREG == 0 && nValuePSINFO > 0 && nValuePSINFO < 11 && nValueSREG != 1 && nValueSREG != 5)
        nValueCREG = 1;

    dwLAC = 0;
    dwCI = 0;
    // There may be additional location information.
    if (MatchStringBeginning(szRsp, ",\"", szRsp))
    {
        (void)ParseHexUInt(szRsp, TRUE, dwLAC, szRsp);
        (void)MatchStringBeginning(szRsp, "\",\"", szRsp);
        (void)ParseHexUInt(szRsp, TRUE, dwCI, szRsp);
        (void)MatchStringBeginning(szRsp, "\"", szRsp);
        // There may be an additional parameter <AcT>
        if (MatchStringBeginning(szRsp, ",", szRsp)) {
            (void)ParseUInt(szRsp, TRUE, dwAcT, szRsp);
        }
    }

    if (!ParseRspPostfix(szRsp, szRsp))
    {
        hr = E_FAIL;
        goto Error;
    }

    // Decide which value to use: if it is
    nValue = nValueSREG;
    if (nValue != 1 && nValue != 5)
        nValue = nValueCREG;

    if (NUM_REGSTATS > nValue) {
        *pdwRegStatus = g_rgdwRegStats[nValue];

    if(g_bFakeRegStatusDeniedToHome && (g_rgdwRegStats[nValue] == RIL_REGSTAT_DENIED))
        g_dwGPRSRegStatus = RIL_REGSTAT_HOME;
    } else {
        *pdwRegStatus = RIL_REGSTAT_UNKNOWN;
    }

    g_dwRegStatus = *pdwRegStatus;
    // Special check (needed for +CREG only): any "+CREG: 3" (access denied) is followed by a "+CREG: 0", which is
    // not correct. As a solution the RIL will still report "access denied" until the module has been registered to a
    // network, the SIM is removed or the module has been resetted.
    // This applies only, when registered to GSM/UMTS.
    if (nValueCREG == nValue) {
        if (g_dwRegStatus == RIL_REGSTAT_DENIED && CREG0_FOLLOWS_CREG3)
            g_fRegStatus3detected = TRUE;
        if ((g_dwRegStatus == RIL_REGSTAT_UNREGISTERED || g_dwRegStatus == RIL_REGSTAT_ATTEMPTING) && g_fRegStatus3detected)
            g_dwRegStatus = RIL_REGSTAT_DENIED;
        if (g_dwRegStatus == RIL_REGSTAT_HOME || g_dwRegStatus == RIL_REGSTAT_ROAMING)
            g_fRegStatus3detected = FALSE;
    }

    if (pDevice != NULL) {
        if (g_dwRegStatus == RIL_REGSTAT_ROAMING && pDevice->IsNoRoamingIndicator())
            g_dwRegStatus = RIL_REGSTAT_HOME;
        if (nValueCREG == nValue) {
            if (g_dwRegStatus == RIL_REGSTAT_ROAMING && g_iLastCOPSOpr != 0 && pDevice->NeedsCheckForNoRoamingNetwork() && pDevice->IsNoRoamingNetwork(g_iLastCOPSOpr))
                g_dwRegStatus = RIL_REGSTAT_HOME;
        }
        *pdwRegStatus = g_dwRegStatus;
        if (pDevice->MustFakeUnregisterdState() && (g_dwRegStatus == RIL_REGSTAT_HOME || g_dwRegStatus == RIL_REGSTAT_ROAMING|| g_dwRegStatus == RIL_REGSTAT_ATTEMPTING))
            *pdwRegStatus = RIL_REGSTAT_UNREGISTERED;
    }

#ifndef RIL_NO_CREG_LOCATION_UPDATES
    if (pDevice != NULL && dwLAC!=0 && dwCI!=0 && (nValue == 1 || nValue == 5))
    {
        CNotificationData *pnd = NULL;
        RILLOCATIONINFO sLI;

        sLI.cbSize = sizeof(RILLOCATIONINFO);
        sLI.dwLocationAreaCode = dwLAC;
        sLI.dwCellID = dwCI;

        pnd = new CNotificationData;
        if (pnd)
        {
            if (!pnd->InitFromRealBlob(RIL_NOTIFY_LOCATIONUPDATE, &sLI, sizeof(RILLOCATIONINFO)))
            {
                delete pnd;
                pnd = NULL;
            }
            pDevice->BroadcastNotification(pnd);
        }
    }
#endif

    pBlob = (void*)pdwRegStatus;
    cbBlob = sizeof(DWORD);

Error:
    if (FAILED(hr)) {
        (void)FreeBlob(pdwRegStatus);
    }
    return hr;
}

//
//
//
HRESULT RILDrv_GetRegistrationStatus(DWORD dwParam)
{
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (pHandle->GetDevice()->GetMultiModeSetting() == MULTIMODE_CONFIG_MULTIMODE) {
        // Module in multimode: check both AT^SREG and AT+CREG and to be sure
        // also AT^SIND=psinfo because of a bug in the PXS8 which might lead
        // to a wrong value of AT+CREG?
        if (!QueueCmd(HCCMD_STANDARD, pHandle, NO_SIND_PSINFO_IN_CDMA?"AT^SREG?;+CREG?\r":"AT^SIND=psinfo,2;^SREG?;+CREG?\r", CMDOPT_NONE, APIID_GETREGISTRATIONSTATUS, ParseGetRegistrationStatusMultiMode, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else if (pHandle->GetDevice()->GetMultiModeSetting() == MULTIMODE_CONFIG_CDMA) {
        // Module in CDMA mode: check AT^SREG instead of AT+CREG
        if (!QueueCmd(HCCMD_STANDARD, pHandle, "AT^SREG?\r", CMDOPT_NONE, APIID_GETREGISTRATIONSTATUS, ParseGetRegistrationStatusCDMA, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }
    else {
        // Default (GSM/UMTS): simply use AT+CREG
        if (!QueueCmd(HCCMD_STANDARD, pHandle, "AT+CREG?\r", CMDOPT_NONE, APIID_GETREGISTRATIONSTATUS, ParseGetRegistrationStatus, NULL, hr)) {
            hr = E_FAIL;
            goto Error;
        }
    }

Error:
    return hr;
}

//
// Comparison routine used for quicksort below
//
static int _cdecl QSCompareMCCNames(const void* pElem1, const void* pElem2)
{
    int nRet;
    const RILOPERATORNAMES* pron1 = (RILOPERATORNAMES*)pElem1;
    const RILOPERATORNAMES* pron2 = (RILOPERATORNAMES*)pElem2;

    BOOL fUnk1 = (0 == strcmp(pron1->szCountryCode, UNKNOWN_COUNTRY_CODE));
    BOOL fUnk2 = (0 == strcmp(pron2->szCountryCode, UNKNOWN_COUNTRY_CODE));
    if (!fUnk1 && fUnk2) {
        nRet = -1;
    } else if (fUnk1 && !fUnk2) {
        nRet = 1;
    } else {
        int iMCCCompare = CompareString(LOCALE_USER_DEFAULT, 0, WideString(pron1->szCountryCode), -1, WideString(pron2->szCountryCode), -1);
        if (CSTR_LESS_THAN == iMCCCompare) {
            nRet = -1;
        } else if (CSTR_GREATER_THAN == iMCCCompare) {
            nRet = 1;
        } else {
            int iLongNameCompare = CompareString(LOCALE_USER_DEFAULT, 0, WideString(pron1->szLongName), -1, WideString(pron2->szLongName), -1);
            if (CSTR_LESS_THAN == iLongNameCompare) {
                nRet = -1;
            } else if (CSTR_GREATER_THAN == iLongNameCompare) {
                nRet = 1;
            } else {
                const char* szNum1 = (strlen(pron1->szNumName) > MCC_LEN) ? ((pron1->szNumName)+MCC_LEN) : "";
                const char* szNum2 = (strlen(pron2->szNumName) > MCC_LEN) ? ((pron2->szNumName)+MCC_LEN) : "";
                int iNumNameCompare = CompareString(LOCALE_USER_DEFAULT, 0, WideString(szNum1), -1, WideString(szNum2), -1);
                if (CSTR_LESS_THAN == iNumNameCompare) {
                    nRet = -1;
                } else if (CSTR_GREATER_THAN == iNumNameCompare) {
                    nRet = 1;
                } else {
                    nRet = 0;
                }
            }
        }
    }

    return nRet;
}


//
//
//
static HRESULT ParseGetAllOperatorsList(LPCSTR /*szRsp*/, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    HRESULT hr = S_OK;
    RILOPERATORNAMES* pron = NULL;
    int i;
    UINT uiBlobSize;
    UINT uiNumOperators = g_nNumOperatorNames;

    // Check: the last operator might be a dummy entry containing the date of
    // the list. This entry should not be included.
    while (uiNumOperators > 0) {
        if (g_rgonmOperatorNames[uiNumOperators-1].nNumericName < PROVIDERLISTINFOKEY)
            break;
        uiNumOperators--;
    }
    uiBlobSize = uiNumOperators * sizeof(RILOPERATORNAMES);

    //ok, copy over the list of all known networks...
    pron = (RILOPERATORNAMES*)AllocBlob(uiBlobSize);
    if (!pron) {
        hr = E_OUTOFMEMORY;
        goto Error;
    }

    for(i = 0; i < (int)uiNumOperators; i++) {
        memset(&(pron[i]), 0x00, sizeof(pron[i]));
        pron[i].cbSize = sizeof(pron[i]);
        //numeric name
        (void)_itoa((int)g_rgonmOperatorNames[i].nNumericName, pron[i].szNumName, 10);
        //long name
        (void)strncpyz(pron[i].szLongName, g_rgonmOperatorNames[i].szLongName, MAXLENGTH_OPERATOR_LONG);
        //MCC ISO country code
        (void)strncpyz(pron[i].szCountryCode, g_rgonmOperatorNames[i].szCountryCode, MAXLENGTH_OPERATOR_COUNTRY_CODE);
        //set valid params
        pron[i].dwParams = RIL_PARAM_ON_LONGNAME|RIL_PARAM_ON_NUMNAME|RIL_PARAM_ON_COUNTRY_CODE;
    }

    //now sort by MCC ISO code
    qsort(pron, uiNumOperators, sizeof(pron[0]), QSCompareMCCNames);

    pBlob = (void*)pron;
    cbBlob = uiBlobSize;

Error:
    if (FAILED(hr)) {
        (void)FreeBlob(pron);
    }
    return hr;
}

HRESULT RILDrv_GetAllOperatorsList(DWORD dwParam)
{
    HRESULT hr = S_OK;

    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETALLOPERATORSLIST, ParseGetAllOperatorsList, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}

//
// GetNetworkListFromFile()
// The network list on the file is a piece of C source code, which has to be
// converted into the internal RIL structures.
//
// Parameter:
// pbBuffe [in]: null-terminated buffer containing the whole file.
//
// Return value: TRUE, if the list has been successfully converted, otherwise FALSE.
//
BOOL GetNetworkListFromFile(char *pbBuffer)
{
    BOOL fRet = FALSE;
    UINT nEntries = 0;
    UINT nEntry = 0;
    UINT nStringBufferSize = 0;
    char *pTmp;
    char *pLine;
    UINT nMaxCCLen;
    char szLine[MAX_PATH];
    OPERATORNAMEMAP *pOperators = NULL;
    char *pStrings = NULL;
    char *pStringBufferPos = NULL;

    // No country code must be longer than the "unknown country code".
    nMaxCCLen = strlen(UNKNOWN_COUNTRY_CODE);

    // 1) step through the file to count the entries to know how much memory
    // is needed for the buffers.
    pTmp = pbBuffer;
    while (pTmp != NULL && *pTmp != '0') {
        // Read the buffer line by line.
        pLine = pTmp;
        pTmp = strstr(pTmp, "\r\n");
        if (pTmp == NULL)
            break;
        pTmp += 2;
        // Ignore all lines longer than MAX_PATH characters. Also ignore all
        // lines too short to contain a network provider entry.
        if (pTmp - pLine < MAX_PATH && pTmp - pLine > 10) {
            char *pString1,*pString2 = NULL;

            memset(szLine, 0, MAX_PATH);
            memcpy(szLine, pLine, pTmp - pLine);
            // Find first string parameter, it contains the network provider
            // name. If there is no name this line might be mal formed.
            pString1 = strchr(szLine, '\"');
            if (pString1 != NULL)
                pString2 = strchr(pString1+1, '\"');
            if (pString2 != NULL) {
                nEntries++;
                // Reserve space for the network provider name.
                nStringBufferSize += ROUNDTODWORD((pString2 - pString1));
                // Reserve space for the country code. All country codes are
                // suppsed to have the same length. The actual length check
                // is performed in step 3.
                nStringBufferSize += ROUNDTODWORD(nMaxCCLen+1);
            }
        }
    }

    // 2) allocate memory for buffers.
    // For performance reasons all strings are put in one string buffer containing
    // all strings for provider name and country code.
    pOperators = (OPERATORNAMEMAP *)AllocBlob(nEntries * sizeof(OPERATORNAMEMAP));
    if (pOperators == NULL)
        goto Error;
    pStrings = (char *)AllocBlob(nStringBufferSize);
    if (pStrings == NULL)
        goto Error;
    memset(pOperators, 0, nEntries * sizeof(OPERATORNAMEMAP));
    memset(pStrings, 0, nStringBufferSize);

    // 3) go through file again and fill buffers.
    pTmp = pbBuffer;
    nEntry = 0;
    pStringBufferPos = pStrings;
    while (pTmp != NULL && *pTmp != '0' && nEntry < nEntries) {
        // Read the buffer line by line, again.
        pLine = pTmp;
        pTmp = strstr(pTmp, "\r\n");
        if (pTmp == NULL)
            break;
        pTmp += 2;
        // Ignore all lines longer than MAX_PATH characters. Also ignore all
        // lines too short to contain a network provider entry.
        if (pTmp - pLine < MAX_PATH && pTmp - pLine > 10) {
            char *pString1,*pString2 = NULL;

            memset(szLine, 0, MAX_PATH);
            memcpy(szLine, pLine, pTmp - pLine);

            // Numeric operator name
            // The numeric operator name follows the open brace. If there is
            // no brace, this file is not formed correctly.
            pString1 = strchr(szLine, '{');
            if (pString1 == NULL)
                goto Error;
            pOperators[nEntry].nNumericName = atoi(pString1+1);

            // Long operator name
            // The long name is put in quotation marks. If there aren't at least
            // two of them, this file is considered to be mal formed.
            pString1 = strchr(szLine, '\"');
            if (pString1 != NULL)
                pString2 = strchr(pString1+1, '\"');
            if (pString2 == NULL)
                goto Error;
            memcpy(pStringBufferPos, pString1+1, pString2-pString1-1);
            pOperators[nEntry].szLongName = pStringBufferPos;
            // Put next strings directly behind this string.
            pStringBufferPos = pStringBufferPos + (ROUNDTODWORD((strlen(pStringBufferPos) + 1)));

            // Country code
            // The country code is a string, put in quotation marks, instead of
            // the string the file might contain the UNKNOWN_COUNTRY_CODE macro.
            if (strstr(szLine, "UNKNOWN_COUNTRY_CODE") == NULL) {
                // The country code is given as a string, not as a macro. If the
                // string is not put between two quotation marks, this file is
                // considered to be malformed.
                pString1 = strchr(pString2+1, '\"');
                if (pString1 != NULL)
                    pString2 = strchr(pString1+1, '\"');
                if (pString2 == NULL)
                    goto Error;
                // All country code are supposed to have the same length. If this
                // is not the case, this file is considered to be mal formed.
                if (pString2-pString1-1 > (INT)nMaxCCLen)
                    goto Error;
                memcpy(pStringBufferPos, pString1+1, pString2-pString1-1);
            }
            else {
                // Use UNKNOWN_COUNTRY_CODE macro as country code.
                memcpy(pStringBufferPos, UNKNOWN_COUNTRY_CODE, nMaxCCLen);
            }
            pOperators[nEntry].szCountryCode= pStringBufferPos;
            // Put next strings directly behind this string.
            pStringBufferPos = pStringBufferPos + (ROUNDTODWORD((strlen(pStringBufferPos) + 1)));

            // Next entry
            nEntry ++;
        }
    }

    fRet = TRUE;
Error:
    if (fRet) {
        // The file has been successfully converted. The RIL can now use this
        // new list instead of the old one. Since this functions is called
        // while the RIL is not handling any IOCTL requests, it is not necessary
        // to do any locking with critical sections since no other thread is
        // accessing the list right now.
        OPERATORNAMEMAP *pOldOperators = NULL;

        // If the last list is from the file, free the old buffer.
        if (s_fListFromFile)
            pOldOperators = (OPERATORNAMEMAP *)g_rgonmOperatorNames;
        // Free the old list of strings, if there was any.
        if (s_szOperatorNamesStringBuffer != NULL)
            FreeBlob(s_szOperatorNamesStringBuffer);
        if (pOldOperators != NULL)
            FreeBlob(pOldOperators);
        // Now set the pointers to the operator list to the buffers of the
        // newly converted list.
        g_rgonmOperatorNames = pOperators;
        s_szOperatorNamesStringBuffer = pStrings;
        g_nNumOperatorNames = nEntries;
    }
    else {
        // The conversions was unsuccessful, so free the buffers, they won't
        // be used anymore.
        if (pOperators != NULL)
            FreeBlob(pOperators);
        if (pStrings != NULL)
            FreeBlob(pStrings);
    }
    return fRet;
}

//
// Searches for a network list file. If found, this function returns file
// name.
// The file name has to have the following format: nwl_xxxx.lst. The
// "xxxx" stands for the file date (free format).
//
// Parameters:
// wszFileName [out]: if file found, name of the network file (full path name)
// nLen [in]: size of wszFileName
//
// Return value: TRUE, if an audio file has been found
//
BOOL CRilHandle::GetNetworkListFilename(WCHAR *wszFileName, UINT nLen) const
{
    BOOL fRet = FALSE;
    WCHAR wszSearchPath[MAX_PATH];
    WCHAR wszTempFileName[MAX_PATH];

    memset(wszSearchPath, 0, sizeof(wszSearchPath));
    memset(wszTempFileName, 0, sizeof(wszSearchPath));
    if (!GetRegistrySZ(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, TEXT("DefaultNwlSearchPath"), wszSearchPath, MAX_PATH)) {
        wcscpy(wszSearchPath, L"\\Storage Card");
    }

    fRet = GetConfigFilename(wszTempFileName, MAX_PATH, L"nwl_*.lst", wszSearchPath);
    if (fRet && wszFileName != NULL)
        (void)wcsncpy(wszFileName, wszTempFileName, nLen);
    return fRet;
}

//
// ReadNetworkListFilename()
// Looks, if there is NWL list on the filesystem. If it differs from the one
// currently in use, this function reads it and transforms it in the internal
// RL format.
//
void CRilHandle::ReadNetworkListFilename()
{
    CNotificationData* pnd = NULL;
    BOOL fSendEndNotification = FALSE;
    HANDLE hFile = INVALID_HANDLE_VALUE;
    DWORD nFileSize = 0;
    DWORD dwRead = 0;
    WCHAR wszFileName[MAX_PATH];
    BYTE *bBuffer = NULL;

    // Search for network file list on device.
    if (!GetNetworkListFilename(wszFileName, MAX_PATH))
        goto Error;
    // Is file already in memory? Don't read it again.
    if (s_fListFromFile) {
        if (wcscmp(s_wszNwlListFile, wszFileName) == 0)
            goto Error;
    }
    hFile = CreateFile(wszFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if (hFile == INVALID_HANDLE_VALUE)
        goto Error;
    pnd = new CNotificationData;
    if (pnd)
    {
        // Send string including terminating '\0'.
        if (pnd->InitFromRealBlob(RIL_NOTIFY_DEVSPECIFIC_NWL_UPDATE_START, (void*)wszFileName, (2*wcslen(wszFileName))+sizeof(WCHAR)))
            BroadcastNotification(pnd);
        else
            delete pnd;
        fSendEndNotification = TRUE;
    }
    // Read whole file in one blok. This allows to RIL to perform the parsing
    // of the list completely in the RAM.
    nFileSize = GetFileSize(hFile, NULL);
    bBuffer = (BYTE *)AllocBlob(nFileSize+1);
    if (bBuffer == NULL)
        goto Error;
    memset(bBuffer, 0, nFileSize+1);
    if(!ReadFile(hFile,bBuffer, nFileSize,&dwRead, NULL))
        goto Error;
    if (dwRead != nFileSize)
        goto Error;

    // The content of the file is now in the buffer, now parse the file.
    if (GetNetworkListFromFile((char *)bBuffer)) {
        // The performs the update in one step, so the progress indicator is just
        // for compatibility with the other progress indications and goes directly
        // to 100%.
        BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_NWL_UPDATE_PROGRESS, 100);
        s_fListFromFile = TRUE;
        wcscpy(s_wszNwlListFile, wszFileName);
        // Send the "OK" notification.
        BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_NWL_UPDATE_END,  UPDATE_PROCESS_SUCCESS);
        fSendEndNotification = FALSE;
    }

Error:
    // If there has been a start notification, but no end notification has been
    // sent so farit means there has been an error, so send out the "error"
    // notification.
    if (fSendEndNotification)
        BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_NWL_UPDATE_END,  UPDATE_PROCESS_ERROR);
    if (hFile != INVALID_HANDLE_VALUE)
        (void)CloseHandle(hFile);
    if (bBuffer != NULL)
        FreeBlob(bBuffer);
    return;
}


//
// Additional Windows Mobile 6.5 RIL-API
//
#if defined(IOCTL_RIL_GETSMSNLTABLELIST)
//
// The following code is unmodified code from the Microsoft Reference RIL.
// Unfortunately it produces some Prefast warnings, which need to be disabled.
// No need for push/pop, there whole rest of this file is Microsoft code.
#ifndef _PREFAST_
#pragma warning(disable: 4068)
#endif
#pragma prefast (disable: 11 31 309 )

static bool GetNextTable(FILE *fFile, TCHAR *pszBuffer, const DWORD cchBuffer)
{
    bool  fTableFound = false;
    TCHAR *pCR = NULL;
    bool fLineSkipped = false;
    const TCHAR* NewLineCharOnly = TEXT("\n");

    if (!pszBuffer || !cchBuffer || !fFile)
    {
        goto Exit;
    }

    *pszBuffer = TEXT('\0');
    do
    {
        // Read characters from the file
        fgetws(pszBuffer, cchBuffer, fFile);
        // Skip newline character only
        fLineSkipped = (0 == _tcsicmp(pszBuffer, NewLineCharOnly));
    }
    while(!feof(fFile) && fLineSkipped);

    if(!fLineSkipped && pszBuffer[0] != TEXT('\0'))
    {
        if(pCR = _tcsstr(pszBuffer, NewLineCharOnly))
        {
            *pCR = TEXT('\0');
            fTableFound = true;
        }
    }

Exit:
    return fTableFound;
}


static HRESULT ReadDecodeTableFromFile(FILE *fFile, WCHAR *ptable)
{
    const int MAX_NUMCHAR_LINE = 16;
    const int MAX_NUMCHAR_COL = 8;
    HRESULT hr = S_OK;

    DEBUGCHK(NULL != ptable);
    DEBUGCHK(NULL != fFile);

    for(int i = 0; i < MAX_NUMCHAR_LINE; i++)
    {
        int idx = i*MAX_NUMCHAR_COL;
        int nFieldsRead = fwscanf(fFile, TEXT("%x %x %x %x %x %x %x %x"), &ptable[idx + 0],
            &ptable[idx + 1], &ptable[idx + 2], &ptable[idx + 3],
            &ptable[idx + 4], &ptable[idx + 5], &ptable[idx + 6], &ptable[idx + 7]);
        if(!nFieldsRead || nFieldsRead == EOF)
        {
            hr = E_FAIL;
            goto Exit;
        }
    }

Exit:
    return hr;
}

static HRESULT ReadEncodeTableFromFile(FILE *fFile, RILSMSCODINGCHARMAP *ptable, UINT *tblLen)
{
    HRESULT hr = S_OK;

    DEBUGCHK(NULL != fFile);
    DEBUGCHK(NULL != ptable);
    DEBUGCHK(NULL != tblLen);

    fwscanf(fFile, TEXT("%d"), tblLen);
    if(*tblLen <= 0 || *tblLen > MAXLENGTH_NLTABLE)
    {
        hr = E_INVALIDARG;
        goto Exit;
    }
    for(UINT i = 0; i < (*tblLen); i++)
    {
        int nFieldsRead = fwscanf(fFile, TEXT("%x %x"), &ptable[i].ch, &ptable[i].wch);
        if(!nFieldsRead || EOF == nFieldsRead)
        {
            hr = E_FAIL;
            goto Exit;
        }
    }

Exit:
    return hr;
}

static HRESULT ReadNLTablesFromFile(TCHAR *pszFileName, RILSMSNLTABLEINFO *prilTableInfo)
{
    FILE    *fFile = NULL;
    HRESULT hr     = S_OK;
    TCHAR   szBuffer[MAX_PATH] = {0};
    bool    fLSDecodeTableLoaded = false;
    bool    fSSDecodeTableLoaded = false;
    bool    fLSEncodeTableLoaded = false;
    bool    fSSEncodeTableLoaded = false;
    DWORD   dwFileAttrs = 0;

    if(!pszFileName || !_tcslen(pszFileName) || !prilTableInfo)
    {
        hr = E_INVALIDARG;
        goto Exit;
    }
    dwFileAttrs = GetFileAttributes(pszFileName);
    if(!(dwFileAttrs & FILE_ATTRIBUTE_SYSTEM)
        && !(dwFileAttrs & FILE_ATTRIBUTE_INROM))
    {
        hr = E_INVALIDARG;
        goto Exit;
    }
    if(NULL == (fFile = _wfopen(pszFileName, L"r")))
    {
        hr = E_INVALIDARG;
        goto Exit;
    }
    while(GetNextTable(fFile, szBuffer, MAX_PATH))
    {
        if(!_tcsicmp(szBuffer, TEXT("LSDecode")))
        {
            if(!fLSDecodeTableLoaded)
            {
                if(SUCCEEDED(ReadDecodeTableFromFile(fFile, prilTableInfo->rgszLSDecodeTable)))
                {
                    prilTableInfo->dwParams |= RIL_PARAM_NL_LSDECODETABLE;
                    fLSDecodeTableLoaded = true;
                }
            }
            else
            {
                DEBUGCHK(!"RilDrv::ReadNLTablesFromFile::LS decode table has been loaded");
            }
        }
        else if(!_tcsicmp(szBuffer, TEXT("SSDecode")))
        {
            if(!fSSDecodeTableLoaded)
            {
                if(SUCCEEDED(ReadDecodeTableFromFile(fFile, prilTableInfo->rgszSSDecodeTable)))
                {
                    prilTableInfo->dwParams |= RIL_PARAM_NL_SSDECODETABLE;
                    fSSDecodeTableLoaded = true;
                }
            }
            else
            {
                DEBUGCHK(!"RilDrv::ReadNLTablesFromFile::SS decode table has been loaded");
            }
        }
        else if(!_tcsicmp(szBuffer, TEXT("LSEncode")))
        {
            if(!fLSEncodeTableLoaded)
            {
                if(SUCCEEDED(ReadEncodeTableFromFile(fFile, prilTableInfo->rgcmLSEncodeTable, &prilTableInfo->nLSEncodeTableLen)))
                {
                    prilTableInfo->dwParams |= RIL_PARAM_NL_LSENCODETABLE;
                    fLSEncodeTableLoaded = true;
                }
            }
            else
            {
                DEBUGCHK(!"RilDrv::ReadNLTablesFromFile::LS encode table has been loaded");
            }
        }
        else if(!_tcsicmp(szBuffer, TEXT("SSEncode")))
        {
            if(!fSSEncodeTableLoaded)
            {
                if(SUCCEEDED(ReadEncodeTableFromFile(fFile, prilTableInfo->rgcmSSEncodeTable, &prilTableInfo->nSSEncodeTableLen)))
                {
                    prilTableInfo->dwParams |= RIL_PARAM_NL_SSENCODETABLE;
                    fSSEncodeTableLoaded = true;
                }
            }
            else
            {
                DEBUGCHK(!"RilDrv::ReadNLTablesFromFile::SS encode table has been loaded");
            }
        }
        else
        {
            DEBUGCHK(!"RilDrv::ReadNLTablesFromFile::Table identifier unrecognized");
        }
    }

    if(!fLSDecodeTableLoaded && !fSSDecodeTableLoaded
        && !fLSEncodeTableLoaded && !fSSEncodeTableLoaded)
    {
        hr = E_FAIL;
    }

Exit:
    if(fFile)
    {
        fclose(fFile);
    }
    return hr;
}

//
//
//
static HRESULT ParseGetSMSNLTableList(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID /*pParam*/)
{
    const LPCTSTR c_szSMSNLDataEncoding   = TEXT("Comm\\Cellular\\Ril\\SMS\\NLTables");
    const LPCTSTR c_szSMSNLNumOfLanguages = TEXT("NumOfLanguages");
    const LPCTSTR c_szSMSNLTableURI       = TEXT("NLTableURI");
    const LPCTSTR c_szSMSNLTableLocales   = TEXT("LocalePrimaryLanguage");

    HRESULT hr = S_OK;
    HKEY  hkeyItems = NULL;
    DWORD dwNumOfLang = 0;
    RILSMSNLTABLEINFO *prilNLTables = NULL;
    UINT uiBlobSize = 0;
    DWORD dwRet = 0;

    //Get the number of supported languages.
    hr = RegistryGetDWORD(HKEY_LOCAL_MACHINE, c_szSMSNLDataEncoding,
        c_szSMSNLNumOfLanguages, &dwNumOfLang);
    if(FAILED(hr))
    {
        goto Error;
    }
    if(0 == dwNumOfLang)
    {
        hr = E_FAIL;
        goto Error;
    }

    uiBlobSize = dwNumOfLang * sizeof(RILSMSNLTABLEINFO);
    prilNLTables = (RILSMSNLTABLEINFO *)AllocBlob(uiBlobSize);
    if(NULL == prilNLTables)
    {
        hr = E_OUTOFMEMORY;
        goto Error;
    }
    memset(prilNLTables, 0, uiBlobSize);

    // Open registry key \\HKLM\\Comm\\Cellular\\Ril\\SMS\\NLTables
    dwRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
        c_szSMSNLDataEncoding, 0, KEY_ENUMERATE_SUB_KEYS, &hkeyItems);
    if(ERROR_SUCCESS != dwRet)
    {
        hr = HRESULT_FROM_WIN32(dwRet);
        goto Error;
    }

    TCHAR szKeyName[MAX_PATH] = {0};
    TCHAR szURI[MAX_PATH] = {0};
    DWORD dwType, dwSize;
    WORD  wPrimaryLanguage;
    DWORD iKey;

    // Each subKey in \\HKLM\\Comm\\Cellular\\Ril\\SMS\\NLTables
    // represents a language with the support of SMS national language encoding/decoding
    // iterate subKeys and get encoding/decoding tables for a language
    for(iKey = 0; iKey < dwNumOfLang; iKey++)
    {
        // Enumerate language subkeys in \\HKLM\\Comm\\Cellular\\Ril\\SMS\\NLTables
        dwSize = MAX_PATH;
        dwRet = RegEnumKeyEx(hkeyItems, iKey,
            szKeyName, &dwSize, NULL, NULL, NULL, NULL);
        if(ERROR_SUCCESS != dwRet)
        {
            if(ERROR_NO_MORE_ITEMS == dwRet)
            {
                // exit when there are no more languages
                hr = S_OK;
            }
            else
            {
                hr = HRESULT_FROM_WIN32(dwRet);
            }
            break;
        }

        BYTE hindex = 0;
        swscanf(szKeyName, TEXT("%d"), &hindex);
        if(0 == hindex)
        {
            continue;
        }

        prilNLTables[iKey].cbSize = sizeof(prilNLTables[iKey]);
        prilNLTables[iKey].langID = hindex;
        prilNLTables[iKey].dwParams |= RIL_PARAM_NL_SMSNLLANGID;

        // Get locale primary language id in \\HKLM\\Comm\\Cellular\\Ril\\NLTables\\[LangID]
        dwSize = sizeof(wPrimaryLanguage);
        dwRet = RegQueryValueEx(hkeyItems, c_szSMSNLTableLocales,
            (DWORD *) szKeyName, &dwType, (LPBYTE)&wPrimaryLanguage, &dwSize);
        if(ERROR_SUCCESS == dwRet)
        {
            DEBUGCHK(dwSize == sizeof(wPrimaryLanguage));
            prilNLTables[iKey].wPrimaryLangID = wPrimaryLanguage;
            prilNLTables[iKey].dwParams |= RIL_PARAM_NL_LOCALELANGID;
        }

        // Get the name of the file which has encoding/decoding tables
        // in \\HKLM\\Comm\\Cellular\\Ril\\SMS\\NLTables\\[langID]
        dwSize = sizeof(szURI);
        dwRet = RegQueryValueEx(hkeyItems, c_szSMSNLTableURI,
            (DWORD *) szKeyName, &dwType, (LPBYTE)szURI, &dwSize);
        if(ERROR_SUCCESS == dwRet)
        {
            // read encoding/decoding table from the specified file
            if(FAILED(ReadNLTablesFromFile(szURI, &prilNLTables[iKey])))
            {
                RILRetailTraceZone(ZONE_ERROR, (TEXT("RilDrv: ParseGetSMSNLTableList: getting tables from %s for language %d failed"), szURI, hindex));
            }
        }
    }

    if(SUCCEEDED(hr))
    {
        pBlob = (void*)prilNLTables;
        cbBlob = uiBlobSize;
    }

Error:
    RegCloseKey(hkeyItems);

    if(FAILED(hr))
    {
        FreeBlob(prilNLTables);
    }
    return hr;
}

HRESULT RILDrv_GetSMSNLTableList(DWORD dwParam)
{
    HRESULT hr = S_OK;
    CRilInstanceHandle* pHandle = ExtractHandle(dwParam);
    if (!pHandle) {
        hr = E_FAIL;
        goto Error;
    }

    if (!QueueCmd(HCCMD_STANDARD, pHandle, NULL, CMDOPT_NOOP, APIID_GETSMSNLTABLELIST, ParseGetSMSNLTableList, NULL, hr)) {
        hr = E_FAIL;
        goto Error;
    }

Error:
    return hr;
}
#endif

