OSDN Git Service

Add CDMA and multimode support to reference-ril
authorJaime A Lopez-Sollano <jaimel@codeaurora.org>
Wed, 29 Aug 2012 14:27:27 +0000 (07:27 -0700)
committerNaveen Kalla <nkalla@codeaurora.org>
Sat, 1 Sep 2012 00:43:33 +0000 (17:43 -0700)
Reference RIL will query emulator modem the supported radio technologies
and the current radio technology and respond to telephony with the
information. Support for basic CDMA RIL request messages and messages
to set and query preferred network mode to be able to switch between
radio technologies.

Change-Id: I950d36691f3e4029f42efe265dc1bb0016b0f410

reference-ril/reference-ril.c

index 578ff63..4b3f926 100644 (file)
@@ -15,6 +15,7 @@
 ** limitations under the License.
 */
 
+#include <telephony/ril_cdma_sms.h>
 #include <stdio.h>
 #include <assert.h>
 #include <string.h>
 #define WORKAROUND_FAKE_CGEV 1
 #endif
 
+/* Modem Technology bits */
+#define MDM_GSM         0x01
+#define MDM_WCDMA       0x02
+#define MDM_CDMA        0x04
+#define MDM_EVDO        0x08
+#define MDM_LTE         0x10
+
+typedef struct {
+    int supportedTechs; // Bitmask of supported Modem Technology bits
+    int currentTech;    // Technology the modem is currently using (in the format used by modem)
+    int isMultimode;
+
+    // Preferred mode bitmask. This is actually 4 byte-sized bitmasks with different priority values,
+    // in which the byte number from LSB to MSB give the priority.
+    //
+    //          |MSB|   |   |LSB
+    // value:   |00 |00 |00 |00
+    // byte #:  |3  |2  |1  |0
+    //
+    // Higher byte order give higher priority. Thus, a value of 0x0000000f represents
+    // a preferred mode of GSM, WCDMA, CDMA, and EvDo in which all are equally preferrable, whereas
+    // 0x00000201 represents a mode with GSM and WCDMA, in which WCDMA is preferred over GSM
+    int32_t preferredNetworkMode;
+    int subscription_source;
+
+} ModemInfo;
+
+static ModemInfo *sMdmInfo;
+// TECH returns the current technology in the format used by the modem.
+// It can be used as an l-value
+#define TECH(mdminfo)                 ((mdminfo)->currentTech)
+// TECH_BIT returns the bitmask equivalent of the current tech
+#define TECH_BIT(mdminfo)            (1 << ((mdminfo)->currentTech))
+#define IS_MULTIMODE(mdminfo)         ((mdminfo)->isMultimode)
+#define TECH_SUPPORTED(mdminfo, tech) ((mdminfo)->supportedTechs & (tech))
+#define PREFERRED_NETWORK(mdminfo)    ((mdminfo)->preferredNetworkMode)
+// CDMA Subscription Source
+#define SSOURCE(mdminfo)              ((mdminfo)->subscription_source)
+
+static int net2modem[] = {
+    MDM_GSM | MDM_WCDMA,                                 // 0  - GSM / WCDMA Pref
+    MDM_GSM,                                             // 1  - GSM only
+    MDM_WCDMA,                                           // 2  - WCDMA only
+    MDM_GSM | MDM_WCDMA,                                 // 3  - GSM / WCDMA Auto
+    MDM_CDMA | MDM_EVDO,                                 // 4  - CDMA / EvDo Auto
+    MDM_CDMA,                                            // 5  - CDMA only
+    MDM_EVDO,                                            // 6  - EvDo only
+    MDM_GSM | MDM_WCDMA | MDM_CDMA | MDM_EVDO,           // 7  - GSM/WCDMA, CDMA, EvDo
+    MDM_LTE | MDM_CDMA | MDM_EVDO,                       // 8  - LTE, CDMA and EvDo
+    MDM_LTE | MDM_GSM | MDM_WCDMA,                       // 9  - LTE, GSM/WCDMA
+    MDM_LTE | MDM_CDMA | MDM_EVDO | MDM_GSM | MDM_WCDMA, // 10 - LTE, CDMA, EvDo, GSM/WCDMA
+    MDM_LTE,                                             // 11 - LTE only
+};
+
+static int32_t net2pmask[] = {
+    MDM_GSM | (MDM_WCDMA << 8),                          // 0  - GSM / WCDMA Pref
+    MDM_GSM,                                             // 1  - GSM only
+    MDM_WCDMA,                                           // 2  - WCDMA only
+    MDM_GSM | MDM_WCDMA,                                 // 3  - GSM / WCDMA Auto
+    MDM_CDMA | MDM_EVDO,                                 // 4  - CDMA / EvDo Auto
+    MDM_CDMA,                                            // 5  - CDMA only
+    MDM_EVDO,                                            // 6  - EvDo only
+    MDM_GSM | MDM_WCDMA | MDM_CDMA | MDM_EVDO,           // 7  - GSM/WCDMA, CDMA, EvDo
+    MDM_LTE | MDM_CDMA | MDM_EVDO,                       // 8  - LTE, CDMA and EvDo
+    MDM_LTE | MDM_GSM | MDM_WCDMA,                       // 9  - LTE, GSM/WCDMA
+    MDM_LTE | MDM_CDMA | MDM_EVDO | MDM_GSM | MDM_WCDMA, // 10 - LTE, CDMA, EvDo, GSM/WCDMA
+    MDM_LTE,                                             // 11 - LTE only
+};
+
+static int is3gpp2(int radioTech) {
+    switch (radioTech) {
+        case RADIO_TECH_IS95A:
+        case RADIO_TECH_IS95B:
+        case RADIO_TECH_1xRTT:
+        case RADIO_TECH_EVDO_0:
+        case RADIO_TECH_EVDO_A:
+        case RADIO_TECH_EVDO_B:
+        case RADIO_TECH_EHRPD:
+            return 1;
+        default:
+            return 0;
+    }
+}
+
 typedef enum {
     SIM_ABSENT = 0,
     SIM_NOT_READY = 1,
     SIM_READY = 2, /* SIM_READY means the radio state is RADIO_STATE_SIM_READY */
     SIM_PIN = 3,
     SIM_PUK = 4,
-    SIM_NETWORK_PERSONALIZATION = 5
+    SIM_NETWORK_PERSONALIZATION = 5,
+    RUIM_ABSENT = 6,
+    RUIM_NOT_READY = 7,
+    RUIM_READY = 8,
+    RUIM_PIN = 9,
+    RUIM_PUK = 10,
+    RUIM_NETWORK_PERSONALIZATION = 11
 } SIM_Status;
 
 static void onRequest (int request, void *data, size_t datalen, RIL_Token t);
@@ -134,6 +225,10 @@ static int s_expectAnswer = 0;
 
 static void pollSIMState (void *param);
 static void setRadioState(RIL_RadioState newState);
+static void setRadioTechnology(ModemInfo *mdm, int newtech);
+static int query_ctec(ModemInfo *mdm, int *current, int32_t *preferred);
+static int parse_technology_response(const char *response, int *current, int32_t *preferred);
+static int techFromModemType(int mdmtype);
 
 static int clccStateToRILState(int state, RIL_CallState *p_state)
 
@@ -273,7 +368,7 @@ static void requestRadioPower(void *data, size_t datalen, RIL_Token t)
                 goto error;
             }
         }
-        setRadioState(RADIO_STATE_SIM_NOT_READY);
+        setRadioState(RADIO_STATE_ON);
     }
 
     at_response_free(p_response);
@@ -763,7 +858,133 @@ error:
     at_response_free(p_response);
 }
 
-static void requestRegistrationState(int request, void *data,
+/**
+ * networkModePossible. Decides whether the network mode is appropriate for the
+ * specified modem
+ */
+static int networkModePossible(ModemInfo *mdm, int nm)
+{
+    if ((net2modem[nm] & mdm->supportedTechs) == net2modem[nm]) {
+       return 1;
+    }
+    return 0;
+}
+static void requestSetPreferredNetworkType( int request, void *data,
+                                            size_t datalen, RIL_Token t )
+{
+    ATResponse *p_response = NULL;
+    char *cmd = NULL;
+    int value = *(int *)data;
+    int current, old;
+    int err;
+    int32_t preferred = net2pmask[value];
+
+    ALOGD("requestSetPreferredNetworkType: current: %x. New: %x", PREFERRED_NETWORK(sMdmInfo), preferred);
+    if (!networkModePossible(sMdmInfo, value)) {
+        RIL_onRequestComplete(t, RIL_E_MODE_NOT_SUPPORTED, NULL, 0);
+        return;
+    }
+    if (query_ctec(sMdmInfo, &current, NULL) < 0) {
+        RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+        return;
+    }
+    old = PREFERRED_NETWORK(sMdmInfo);
+    ALOGD("old != preferred: %d", old != preferred);
+    if (old != preferred) {
+        asprintf(&cmd, "AT+CTEC=%d,\"%x\"", current, preferred);
+        ALOGD("Sending command: <%s>", cmd);
+        err = at_send_command_singleline(cmd, "+CTEC:", &p_response);
+        free(cmd);
+        if (err || !p_response->success) {
+            RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+            return;
+        }
+        PREFERRED_NETWORK(sMdmInfo) = value;
+        if (!strstr( p_response->p_intermediates->line, "DONE") ) {
+            int current;
+            int res = parse_technology_response(p_response->p_intermediates->line, &current, NULL);
+            switch (res) {
+                case -1: // Error or unable to parse
+                    break;
+                case 1: // Only able to parse current
+                case 0: // Both current and preferred were parsed
+                    setRadioTechnology(sMdmInfo, current);
+                    break;
+            }
+        }
+    }
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+static void requestGetPreferredNetworkType(int request, void *data,
+                                   size_t datalen, RIL_Token t)
+{
+    int preferred;
+    unsigned i;
+
+    switch ( query_ctec(sMdmInfo, NULL, &preferred) ) {
+        case -1: // Error or unable to parse
+        case 1: // Only able to parse current
+            RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+            break;
+        case 0: // Both current and preferred were parsed
+            for ( i = 0 ; i < sizeof(net2pmask) / sizeof(int32_t) ; i++ ) {
+                if (preferred == net2pmask[i]) {
+                    RIL_onRequestComplete(t, RIL_E_SUCCESS, &i, sizeof(int));
+                    return;
+                }
+            }
+            ALOGE("Unknown preferred mode received from modem: %d", preferred);
+            RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+            break;
+    }
+
+}
+
+static void requestCdmaPrlVersion(int request, void *data,
+                                   size_t datalen, RIL_Token t)
+{
+    int err;
+    char * responseStr;
+    ATResponse *p_response = NULL;
+    const char *cmd;
+    char *line;
+
+    err = at_send_command_singleline("AT+WPRL?", "+WPRL:", &p_response);
+    if (err < 0 || !p_response->success) goto error;
+    line = p_response->p_intermediates->line;
+    err = at_tok_start(&line);
+    if (err < 0) goto error;
+    err = at_tok_nextstr(&line, &responseStr);
+    if (err < 0 || !responseStr) goto error;
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, responseStr, strlen(responseStr));
+    at_response_free(p_response);
+    return;
+error:
+    at_response_free(p_response);
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+}
+
+static void requestCdmaBaseBandVersion(int request, void *data,
+                                   size_t datalen, RIL_Token t)
+{
+    int err;
+    char * responseStr;
+    ATResponse *p_response = NULL;
+    const char *cmd;
+    const char *prefix;
+    char *line, *p;
+    int commas;
+    int skip;
+    int count = 4;
+
+    // Fixed values. TODO: query modem
+    responseStr = strdup("1.0.0.0");
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, responseStr, sizeof(responseStr));
+    free(responseStr);
+}
+
+static void requestCdmaDeviceIdentity(int request, void *data,
                                         size_t datalen, RIL_Token t)
 {
     int err;
@@ -775,26 +996,184 @@ static void requestRegistrationState(int request, void *data,
     char *line, *p;
     int commas;
     int skip;
-    int count = 3;
+    int count = 4;
 
+    // Fixed values. TODO: Query modem
+    responseStr[0] = "----";
+    responseStr[1] = "----";
+    responseStr[2] = "77777777";
 
-    if (request == RIL_REQUEST_VOICE_REGISTRATION_STATE) {
-        cmd = "AT+CREG?";
-        prefix = "+CREG:";
-    } else if (request == RIL_REQUEST_DATA_REGISTRATION_STATE) {
-        cmd = "AT+CGREG?";
-        prefix = "+CGREG:";
+    err = at_send_command_numeric("AT+CGSN", &p_response);
+    if (err < 0 || p_response->success == 0) {
+        RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+        return;
     } else {
-        assert(0);
+        responseStr[3] = p_response->p_intermediates->line;
+    }
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, responseStr, count*sizeof(char*));
+    at_response_free(p_response);
+
+    return;
+error:
+    ALOGE("requestCdmaDeviceIdentity must never return an error when radio is on");
+    at_response_free(p_response);
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+}
+
+static void requestCdmaGetSubscriptionSource(int request, void *data,
+                                        size_t datalen, RIL_Token t)
+{
+    int err;
+    int *ss = (int *)data;
+    ATResponse *p_response = NULL;
+    char *cmd = NULL;
+    char *line = NULL;
+    int response;
+
+    asprintf(&cmd, "AT+CCSS?");
+    if (!cmd) goto error;
+
+    err = at_send_command_singleline(cmd, "+CCSS:", &p_response);
+    if (err < 0 || !p_response->success)
         goto error;
+
+    line = p_response->p_intermediates->line;
+    err = at_tok_start(&line);
+    if (err < 0) goto error;
+
+    err = at_tok_nextint(&line, &response);
+    free(cmd);
+    cmd = NULL;
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, &response, sizeof(response));
+
+    return;
+error:
+    free(cmd);
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+}
+
+static void requestCdmaSetSubscriptionSource(int request, void *data,
+                                        size_t datalen, RIL_Token t)
+{
+    int err;
+    int *ss = (int *)data;
+    ATResponse *p_response = NULL;
+    char *cmd = NULL;
+
+    if (!ss || !datalen) {
+        ALOGE("RIL_REQUEST_CDMA_SET_SUBSCRIPTION without data!");
+        RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+        return;
     }
+    asprintf(&cmd, "AT+CCSS=%d", ss[0]);
+    if (!cmd) goto error;
 
-    err = at_send_command_singleline(cmd, prefix, &p_response);
+    err = at_send_command(cmd, &p_response);
+    if (err < 0 || !p_response->success)
+        goto error;
+    free(cmd);
+    cmd = NULL;
 
-    if (err != 0) goto error;
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
 
+    RIL_onUnsolicitedResponse(RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED, ss, sizeof(ss[0]));
+
+    return;
+error:
+    free(cmd);
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+}
+
+static void requestCdmaSubscription(int request, void *data,
+                                        size_t datalen, RIL_Token t)
+{
+    int err;
+    int response[5];
+    char * responseStr[5];
+    ATResponse *p_response = NULL;
+    const char *cmd;
+    const char *prefix;
+    char *line, *p;
+    int commas;
+    int skip;
+    int count = 5;
+
+    // Fixed values. TODO: Query modem
+    responseStr[0] = "8587777777"; // MDN
+    responseStr[1] = "1"; // SID
+    responseStr[2] = "1"; // NID
+    responseStr[3] = "8587777777"; // MIN
+    responseStr[4] = "1"; // PRL Version
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, responseStr, count*sizeof(char*));
+
+    return;
+error:
+    ALOGE("requestRegistrationState must never return an error when radio is on");
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+}
+
+static void requestCdmaGetRoamingPreference(int request, void *data,
+                                                 size_t datalen, RIL_Token t)
+{
+    int roaming_pref = -1;
+    ATResponse *p_response = NULL;
+    char *line;
+    int res;
+
+    res = at_send_command_singleline("AT+WRMP?", "+WRMP:", &p_response);
+    if (res < 0 || !p_response->success) {
+        goto error;
+    }
     line = p_response->p_intermediates->line;
 
+    res = at_tok_start(&line);
+    if (res < 0) goto error;
+
+    res = at_tok_nextint(&line, &roaming_pref);
+    if (res < 0) goto error;
+
+     RIL_onRequestComplete(t, RIL_E_SUCCESS, &roaming_pref, sizeof(roaming_pref));
+    return;
+error:
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+}
+
+static void requestCdmaSetRoamingPreference(int request, void *data,
+                                                 size_t datalen, RIL_Token t)
+{
+    int *pref = (int *)data;
+    ATResponse *p_response = NULL;
+    char *line;
+    int res;
+    char *cmd = NULL;
+
+    asprintf(&cmd, "AT+WRMP=%d", *pref);
+    if (cmd == NULL) goto error;
+
+    res = at_send_command(cmd, &p_response);
+    if (res < 0 || !p_response->success)
+        goto error;
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+    free(cmd);
+    return;
+error:
+    free(cmd);
+    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+}
+
+static int parseRegistrationState(char *str, int *type, int *items, int **response)
+{
+    int err;
+    char *line = str, *p;
+    int *resp = NULL;
+    int skip;
+    int count = 3;
+    int commas;
+
+    ALOGD("parseRegistrationState. Parsing: %s",str);
     err = at_tok_start(&line);
     if (err < 0) goto error;
 
@@ -825,40 +1204,42 @@ static void requestRegistrationState(int request, void *data,
         if (*p == ',') commas++;
     }
 
+    resp = (int *)calloc(commas + 1, sizeof(int));
+    if (!resp) goto error;
     switch (commas) {
         case 0: /* +CREG: <stat> */
-            err = at_tok_nextint(&line, &response[0]);
+            err = at_tok_nextint(&line, &resp[0]);
             if (err < 0) goto error;
-            response[1] = -1;
-            response[2] = -1;
+            resp[1] = -1;
+            resp[2] = -1;
         break;
 
         case 1: /* +CREG: <n>, <stat> */
             err = at_tok_nextint(&line, &skip);
             if (err < 0) goto error;
-            err = at_tok_nextint(&line, &response[0]);
+            err = at_tok_nextint(&line, &resp[0]);
             if (err < 0) goto error;
-            response[1] = -1;
-            response[2] = -1;
+            resp[1] = -1;
+            resp[2] = -1;
             if (err < 0) goto error;
         break;
 
         case 2: /* +CREG: <stat>, <lac>, <cid> */
-            err = at_tok_nextint(&line, &response[0]);
+            err = at_tok_nextint(&line, &resp[0]);
             if (err < 0) goto error;
-            err = at_tok_nexthexint(&line, &response[1]);
+            err = at_tok_nexthexint(&line, &resp[1]);
             if (err < 0) goto error;
-            err = at_tok_nexthexint(&line, &response[2]);
+            err = at_tok_nexthexint(&line, &resp[2]);
             if (err < 0) goto error;
         break;
         case 3: /* +CREG: <n>, <stat>, <lac>, <cid> */
             err = at_tok_nextint(&line, &skip);
             if (err < 0) goto error;
-            err = at_tok_nextint(&line, &response[0]);
+            err = at_tok_nextint(&line, &resp[0]);
             if (err < 0) goto error;
-            err = at_tok_nexthexint(&line, &response[1]);
+            err = at_tok_nexthexint(&line, &resp[1]);
             if (err < 0) goto error;
-            err = at_tok_nexthexint(&line, &response[2]);
+            err = at_tok_nexthexint(&line, &resp[2]);
             if (err < 0) goto error;
         break;
         /* special case for CGREG, there is a fourth parameter
@@ -867,32 +1248,142 @@ static void requestRegistrationState(int request, void *data,
         case 4: /* +CGREG: <n>, <stat>, <lac>, <cid>, <networkType> */
             err = at_tok_nextint(&line, &skip);
             if (err < 0) goto error;
-            err = at_tok_nextint(&line, &response[0]);
+            err = at_tok_nextint(&line, &resp[0]);
             if (err < 0) goto error;
-            err = at_tok_nexthexint(&line, &response[1]);
+            err = at_tok_nexthexint(&line, &resp[1]);
             if (err < 0) goto error;
-            err = at_tok_nexthexint(&line, &response[2]);
+            err = at_tok_nexthexint(&line, &resp[2]);
             if (err < 0) goto error;
-            err = at_tok_nexthexint(&line, &response[3]);
+            err = at_tok_nexthexint(&line, &resp[3]);
             if (err < 0) goto error;
             count = 4;
         break;
         default:
             goto error;
     }
+    if (response)
+        *response = resp;
+    if (items)
+        *items = commas + 1;
+    if (type)
+        *type = techFromModemType(TECH(sMdmInfo));
+    return 0;
+error:
+    free(resp);
+    return -1;
+}
 
-    asprintf(&responseStr[0], "%d", response[0]);
-    asprintf(&responseStr[1], "%x", response[1]);
-    asprintf(&responseStr[2], "%x", response[2]);
+#define REG_STATE_LEN 15
+#define REG_DATA_STATE_LEN 6
+static void requestRegistrationState(int request, void *data,
+                                        size_t datalen, RIL_Token t)
+{
+    int err;
+    int *registration;
+    char **responseStr = NULL;
+    ATResponse *p_response = NULL;
+    const char *cmd;
+    const char *prefix;
+    char *line;
+    int i = 0, j, numElements = 0;
+    int count = 3;
+    int type, startfrom;
 
-    if (count > 3)
-        asprintf(&responseStr[3], "%d", response[3]);
+    ALOGD("requestRegistrationState");
+    if (request == RIL_REQUEST_VOICE_REGISTRATION_STATE) {
+        cmd = "AT+CREG?";
+        prefix = "+CREG:";
+        numElements = REG_STATE_LEN;
+    } else if (request == RIL_REQUEST_DATA_REGISTRATION_STATE) {
+        cmd = "AT+CGREG?";
+        prefix = "+CGREG:";
+        numElements = REG_DATA_STATE_LEN;
+    } else {
+        assert(0);
+        goto error;
+    }
 
-    RIL_onRequestComplete(t, RIL_E_SUCCESS, responseStr, count*sizeof(char*));
+    err = at_send_command_singleline(cmd, prefix, &p_response);
+
+    if (err != 0) goto error;
+
+    line = p_response->p_intermediates->line;
+
+    if (parseRegistrationState(line, &type, &count, &registration)) goto error;
+
+    responseStr = malloc(numElements * sizeof(char *));
+    if (!responseStr) goto error;
+    memset(responseStr, 0, numElements * sizeof(char *));
+    /**
+     * The first '4' bytes for both registration states remain the same.
+     * But if the request is 'DATA_REGISTRATION_STATE',
+     * the 5th and 6th byte(s) are optional.
+     */
+    if (is3gpp2(type) == 1) {
+        ALOGD("registration state type: 3GPP2");
+        // TODO: Query modem
+        startfrom = 3;
+        if(request == RIL_REQUEST_VOICE_REGISTRATION_STATE) {
+            asprintf(&responseStr[3], "8");     // EvDo revA
+            asprintf(&responseStr[4], "1");     // BSID
+            asprintf(&responseStr[5], "123");   // Latitude
+            asprintf(&responseStr[6], "222");   // Longitude
+            asprintf(&responseStr[7], "0");     // CSS Indicator
+            asprintf(&responseStr[8], "4");     // SID
+            asprintf(&responseStr[9], "65535"); // NID
+            asprintf(&responseStr[10], "0");    // Roaming indicator
+            asprintf(&responseStr[11], "1");    // System is in PRL
+            asprintf(&responseStr[12], "0");    // Default Roaming indicator
+            asprintf(&responseStr[13], "0");    // Reason for denial
+            asprintf(&responseStr[14], "0");    // Primary Scrambling Code of Current cell
+      } else if (request == RIL_REQUEST_DATA_REGISTRATION_STATE) {
+            asprintf(&responseStr[3], "8");   // Available data radio technology
+      }
+    } else { // type == RADIO_TECH_3GPP
+        ALOGD("registration state type: 3GPP");
+        startfrom = 0;
+        asprintf(&responseStr[1], "%x", registration[1]);
+        asprintf(&responseStr[2], "%x", registration[2]);
+        if (count > 3)
+            asprintf(&responseStr[3], "%d", registration[3]);
+    }
+    asprintf(&responseStr[0], "%d", registration[0]);
+
+    /**
+     * Optional bytes for DATA_REGISTRATION_STATE request
+     * 4th byte : Registration denial code
+     * 5th byte : The max. number of simultaneous Data Calls
+     */
+    if(request == RIL_REQUEST_DATA_REGISTRATION_STATE) {
+        // asprintf(&responseStr[4], "3");
+        // asprintf(&responseStr[5], "1");
+    }
+
+    for (j = startfrom; j < numElements; j++) {
+        if (!responseStr[i]) goto error;
+    }
+    free(registration);
+    registration = NULL;
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, responseStr, numElements*sizeof(responseStr));
+    for (j = 0; j < numElements; j++ ) {
+        free(responseStr[j]);
+        responseStr[j] = NULL;
+    }
+    free(responseStr);
+    responseStr = NULL;
     at_response_free(p_response);
 
     return;
 error:
+    if (responseStr) {
+        for (j = 0; j < numElements; j++) {
+            free(responseStr[j]);
+            responseStr[j] = NULL;
+        }
+        free(responseStr);
+        responseStr = NULL;
+    }
     ALOGE("requestRegistrationState must never return an error when radio is on");
     RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
     at_response_free(p_response);
@@ -969,6 +1460,38 @@ error:
     at_response_free(p_response);
 }
 
+static void requestCdmaSendSMS(void *data, size_t datalen, RIL_Token t)
+{
+    int err = 1; // Set to go to error:
+    RIL_SMS_Response response;
+    RIL_CDMA_SMS_Message* rcsm;
+
+    ALOGD("requestCdmaSendSMS datalen=%d, sizeof(RIL_CDMA_SMS_Message)=%d",
+            datalen, sizeof(RIL_CDMA_SMS_Message));
+
+    // verify data content to test marshalling/unmarshalling:
+    rcsm = (RIL_CDMA_SMS_Message*)data;
+    ALOGD("TeleserviceID=%d, bIsServicePresent=%d, \
+            uServicecategory=%d, sAddress.digit_mode=%d, \
+            sAddress.Number_mode=%d, sAddress.number_type=%d, ",
+            rcsm->uTeleserviceID,  rcsm->bIsServicePresent,
+            rcsm->uServicecategory,rcsm->sAddress.digit_mode,
+            rcsm->sAddress.number_mode,rcsm->sAddress.number_type);
+
+    if (err != 0) goto error;
+
+    // Cdma Send SMS implementation will go here:
+    // But it is not implemented yet.
+
+    memset(&response, 0, sizeof(response));
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, &response, sizeof(response));
+    return;
+
+error:
+    // Cdma Send SMS will always cause send retry error.
+    RIL_onRequestComplete(t, RIL_E_SMS_SEND_FAIL_RETRY, NULL, 0);
+}
+
 static void requestSendSMS(void *data, size_t datalen, RIL_Token t)
 {
     int err;
@@ -1253,6 +1776,44 @@ static void  requestSendUSSD(void *data, size_t datalen, RIL_Token t)
 
 }
 
+static void requestExitEmergencyMode(void *data, size_t datalen, RIL_Token t)
+{
+    int err;
+    ATResponse *p_response = NULL;
+
+    err = at_send_command("AT+WSOS=0", &p_response);
+
+    if (err < 0 || p_response->success == 0) {
+        RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+        return;
+    }
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
+// TODO: Use all radio types
+static int techFromModemType(int mdmtype)
+{
+    int ret = -1;
+    switch (1 << mdmtype) {
+        case MDM_CDMA:
+            ret = RADIO_TECH_1xRTT;
+            break;
+        case MDM_EVDO:
+            ret = RADIO_TECH_EVDO_A;
+            break;
+        case MDM_GSM:
+            ret = RADIO_TECH_GPRS;
+            break;
+        case MDM_WCDMA:
+            ret = RADIO_TECH_HSPA;
+            break;
+        case MDM_LTE:
+            ret = RADIO_TECH_LTE;
+            break;
+    }
+    return ret;
+}
 
 /*** Callback methods from the RIL library to us ***/
 
@@ -1431,6 +1992,9 @@ onRequest (int request, void *data, size_t datalen, RIL_Token t)
         case RIL_REQUEST_SEND_SMS:
             requestSendSMS(data, datalen, t);
             break;
+        case RIL_REQUEST_CDMA_SEND_SMS:
+            requestCdmaSendSMS(data, datalen, t);
+            break;
         case RIL_REQUEST_SETUP_DATA_CALL:
             requestSetupDataCall(data, datalen, t);
             break;
@@ -1548,7 +2112,74 @@ onRequest (int request, void *data, size_t datalen, RIL_Token t)
             requestEnterSimPin(data, datalen, t);
             break;
 
+        case RIL_REQUEST_VOICE_RADIO_TECH:
+            {
+                int tech = techFromModemType(TECH(sMdmInfo));
+                if (tech < 0 )
+                    RIL_onRequestComplete(t, RIL_E_GENERIC_FAILURE, NULL, 0);
+                else
+                    RIL_onRequestComplete(t, RIL_E_SUCCESS, &tech, sizeof(tech));
+            }
+            break;
+        case RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE:
+            requestSetPreferredNetworkType(request, data, datalen, t);
+            break;
+
+        case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE:
+            requestGetPreferredNetworkType(request, data, datalen, t);
+            break;
+
+        /* CDMA Specific Requests */
+        case RIL_REQUEST_BASEBAND_VERSION:
+            if (TECH_BIT(sMdmInfo) == MDM_CDMA) {
+                requestCdmaBaseBandVersion(request, data, datalen, t);
+                break;
+            } // Fall-through if tech is not cdma
+
+        case RIL_REQUEST_DEVICE_IDENTITY:
+            if (TECH_BIT(sMdmInfo) == MDM_CDMA) {
+                requestCdmaDeviceIdentity(request, data, datalen, t);
+                break;
+            } // Fall-through if tech is not cdma
+
+        case RIL_REQUEST_CDMA_SUBSCRIPTION:
+            if (TECH_BIT(sMdmInfo) == MDM_CDMA) {
+                requestCdmaSubscription(request, data, datalen, t);
+                break;
+            } // Fall-through if tech is not cdma
+
+        case RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE:
+            if (TECH_BIT(sMdmInfo) == MDM_CDMA) {
+                requestCdmaSetSubscriptionSource(request, data, datalen, t);
+                break;
+            } // Fall-through if tech is not cdma
+
+        case RIL_REQUEST_CDMA_GET_SUBSCRIPTION_SOURCE:
+            if (TECH_BIT(sMdmInfo) == MDM_CDMA) {
+                requestCdmaGetSubscriptionSource(request, data, datalen, t);
+                break;
+            } // Fall-through if tech is not cdma
+
+        case RIL_REQUEST_CDMA_QUERY_ROAMING_PREFERENCE:
+            if (TECH_BIT(sMdmInfo) == MDM_CDMA) {
+                requestCdmaGetRoamingPreference(request, data, datalen, t);
+                break;
+            } // Fall-through if tech is not cdma
+
+        case RIL_REQUEST_CDMA_SET_ROAMING_PREFERENCE:
+            if (TECH_BIT(sMdmInfo) == MDM_CDMA) {
+                requestCdmaSetRoamingPreference(request, data, datalen, t);
+                break;
+            } // Fall-through if tech is not cdma
+
+        case RIL_REQUEST_EXIT_EMERGENCY_CALLBACK_MODE:
+            if (TECH_BIT(sMdmInfo) == MDM_CDMA) {
+                requestExitEmergencyMode(data, datalen, t);
+                break;
+            } // Fall-through if tech is not cdma
+
         default:
+            ALOGD("Request not supported. Tech: %d",TECH(sMdmInfo));
             RIL_onRequestComplete(t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0);
             break;
     }
@@ -1590,8 +2221,29 @@ static const char * getVersion(void)
 }
 
 static void
+setRadioTechnology(ModemInfo *mdm, int newtech)
+{
+    ALOGD("setRadioTechnology(%d)", newtech);
+
+    int oldtech = TECH(mdm);
+
+    if (newtech != oldtech) {
+        ALOGD("Tech change (%d => %d)", oldtech, newtech);
+        TECH(mdm) = newtech;
+        if (techFromModemType(newtech) != techFromModemType(oldtech)) {
+            int tech = techFromModemType(TECH(sMdmInfo));
+            if (tech > 0 ) {
+                RIL_onUnsolicitedResponse(RIL_UNSOL_VOICE_RADIO_TECH_CHANGED,
+                                          &tech, sizeof(tech));
+            }
+        }
+    }
+}
+
+static void
 setRadioState(RIL_RadioState newState)
 {
+    ALOGD("setRadioState(%d)", newState);
     RIL_RadioState oldState;
 
     pthread_mutex_lock(&s_state_mutex);
@@ -1626,14 +2278,89 @@ setRadioState(RIL_RadioState newState)
          * Currently, this doesn't happen, but if that changes then these
          * will need to be dispatched on the request thread
          */
-        if (sState == RADIO_STATE_SIM_READY) {
-            onSIMReady();
-        } else if (sState == RADIO_STATE_SIM_NOT_READY) {
+        if (sState == RADIO_STATE_ON) {
             onRadioPowerOn();
         }
     }
 }
 
+/** Returns RUIM_NOT_READY on error */
+static SIM_Status
+getRUIMStatus()
+{
+    ATResponse *p_response = NULL;
+    int err;
+    int ret;
+    char *cpinLine;
+    char *cpinResult;
+
+    if (sState == RADIO_STATE_OFF || sState == RADIO_STATE_UNAVAILABLE) {
+        ret = SIM_NOT_READY;
+        goto done;
+    }
+
+    err = at_send_command_singleline("AT+CPIN?", "+CPIN:", &p_response);
+
+    if (err != 0) {
+        ret = SIM_NOT_READY;
+        goto done;
+    }
+
+    switch (at_get_cme_error(p_response)) {
+        case CME_SUCCESS:
+            break;
+
+        case CME_SIM_NOT_INSERTED:
+            ret = SIM_ABSENT;
+            goto done;
+
+        default:
+            ret = SIM_NOT_READY;
+            goto done;
+    }
+
+    /* CPIN? has succeeded, now look at the result */
+
+    cpinLine = p_response->p_intermediates->line;
+    err = at_tok_start (&cpinLine);
+
+    if (err < 0) {
+        ret = SIM_NOT_READY;
+        goto done;
+    }
+
+    err = at_tok_nextstr(&cpinLine, &cpinResult);
+
+    if (err < 0) {
+        ret = SIM_NOT_READY;
+        goto done;
+    }
+
+    if (0 == strcmp (cpinResult, "SIM PIN")) {
+        ret = SIM_PIN;
+        goto done;
+    } else if (0 == strcmp (cpinResult, "SIM PUK")) {
+        ret = SIM_PUK;
+        goto done;
+    } else if (0 == strcmp (cpinResult, "PH-NET PIN")) {
+        return SIM_NETWORK_PERSONALIZATION;
+    } else if (0 != strcmp (cpinResult, "READY"))  {
+        /* we're treating unsupported lock types as "sim absent" */
+        ret = SIM_ABSENT;
+        goto done;
+    }
+
+    at_response_free(p_response);
+    p_response = NULL;
+    cpinResult = NULL;
+
+    ret = SIM_READY;
+
+done:
+    at_response_free(p_response);
+    return ret;
+}
+
 /** Returns SIM_NOT_READY on error */
 static SIM_Status
 getSIMStatus()
@@ -1644,6 +2371,7 @@ getSIMStatus()
     char *cpinLine;
     char *cpinResult;
 
+    ALOGD("getSIMStatus(). sState: %d",sState);
     if (sState == RADIO_STATE_OFF || sState == RADIO_STATE_UNAVAILABLE) {
         ret = SIM_NOT_READY;
         goto done;
@@ -1737,7 +2465,25 @@ static int getCardStatus(RIL_CardStatus_v6 **pp_card_status) {
           NULL, NULL, 0, RIL_PINSTATE_ENABLED_BLOCKED, RIL_PINSTATE_UNKNOWN },
         // SIM_NETWORK_PERSONALIZATION = 5
         { RIL_APPTYPE_SIM, RIL_APPSTATE_SUBSCRIPTION_PERSO, RIL_PERSOSUBSTATE_SIM_NETWORK,
-          NULL, NULL, 0, RIL_PINSTATE_ENABLED_NOT_VERIFIED, RIL_PINSTATE_UNKNOWN }
+          NULL, NULL, 0, RIL_PINSTATE_ENABLED_NOT_VERIFIED, RIL_PINSTATE_UNKNOWN },
+        // RUIM_ABSENT = 6
+        { RIL_APPTYPE_UNKNOWN, RIL_APPSTATE_UNKNOWN, RIL_PERSOSUBSTATE_UNKNOWN,
+          NULL, NULL, 0, RIL_PINSTATE_UNKNOWN, RIL_PINSTATE_UNKNOWN },
+        // RUIM_NOT_READY = 7
+        { RIL_APPTYPE_RUIM, RIL_APPSTATE_DETECTED, RIL_PERSOSUBSTATE_UNKNOWN,
+          NULL, NULL, 0, RIL_PINSTATE_UNKNOWN, RIL_PINSTATE_UNKNOWN },
+        // RUIM_READY = 8
+        { RIL_APPTYPE_RUIM, RIL_APPSTATE_READY, RIL_PERSOSUBSTATE_READY,
+          NULL, NULL, 0, RIL_PINSTATE_UNKNOWN, RIL_PINSTATE_UNKNOWN },
+        // RUIM_PIN = 9
+        { RIL_APPTYPE_RUIM, RIL_APPSTATE_PIN, RIL_PERSOSUBSTATE_UNKNOWN,
+          NULL, NULL, 0, RIL_PINSTATE_ENABLED_NOT_VERIFIED, RIL_PINSTATE_UNKNOWN },
+        // RUIM_PUK = 10
+        { RIL_APPTYPE_RUIM, RIL_APPSTATE_PUK, RIL_PERSOSUBSTATE_UNKNOWN,
+          NULL, NULL, 0, RIL_PINSTATE_ENABLED_BLOCKED, RIL_PINSTATE_UNKNOWN },
+        // RUIM_NETWORK_PERSONALIZATION = 11
+        { RIL_APPTYPE_RUIM, RIL_APPSTATE_SUBSCRIPTION_PERSO, RIL_PERSOSUBSTATE_SIM_NETWORK,
+           NULL, NULL, 0, RIL_PINSTATE_ENABLED_NOT_VERIFIED, RIL_PINSTATE_UNKNOWN }
     };
     RIL_CardState card_state;
     int num_apps;
@@ -1748,7 +2494,7 @@ static int getCardStatus(RIL_CardStatus_v6 **pp_card_status) {
         num_apps = 0;
     } else {
         card_state = RIL_CARDSTATE_PRESENT;
-        num_apps = 1;
+        num_apps = 2;
     }
 
     // Allocate and initialize base card status.
@@ -1770,11 +2516,13 @@ static int getCardStatus(RIL_CardStatus_v6 **pp_card_status) {
     // that reflects sim_status for gsm.
     if (num_apps != 0) {
         // Only support one app, gsm
-        p_card_status->num_applications = 1;
+        p_card_status->num_applications = 2;
         p_card_status->gsm_umts_subscription_app_index = 0;
+        p_card_status->cdma_subscription_app_index = 1;
 
         // Get the correct app status
         p_card_status->applications[0] = app_status_array[sim_status];
+        p_card_status->applications[1] = app_status_array[sim_status + RUIM_ABSENT];
     }
 
     *pp_card_status = p_card_status;
@@ -1810,7 +2558,8 @@ static void pollSIMState (void *param)
         case SIM_PUK:
         case SIM_NETWORK_PERSONALIZATION:
         default:
-            setRadioState(RADIO_STATE_SIM_LOCKED_OR_ABSENT);
+            ALOGI("SIM ABSENT or LOCKED");
+            RIL_onUnsolicitedResponse(RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED, NULL, 0);
         return;
 
         case SIM_NOT_READY:
@@ -1818,7 +2567,9 @@ static void pollSIMState (void *param)
         return;
 
         case SIM_READY:
-            setRadioState(RADIO_STATE_SIM_READY);
+            ALOGI("SIM_READY");
+            onSIMReady();
+            RIL_onUnsolicitedResponse(RIL_UNSOL_RESPONSE_SIM_STATUS_CHANGED, NULL, 0);
         return;
     }
 }
@@ -1857,6 +2608,168 @@ error:
 }
 
 /**
+ * Parse the response generated by a +CTEC AT command
+ * The values read from the response are stored in current and preferred.
+ * Both current and preferred may be null. The corresponding value is ignored in that case.
+ *
+ * @return: -1 if some error occurs (or if the modem doesn't understand the +CTEC command)
+ *          1 if the response includes the current technology only
+ *          0 if the response includes both current technology and preferred mode
+ */
+int parse_technology_response( const char *response, int *current, int32_t *preferred )
+{
+    int err;
+    char *line, *p;
+    int ct;
+    int32_t pt = 0;
+    char *str_pt;
+
+    line = p = strdup(response);
+    ALOGD("Response: %s", line);
+    err = at_tok_start(&p);
+    if (err || !at_tok_hasmore(&p)) {
+        ALOGD("err: %d. p: %s", err, p);
+        free(line);
+        return -1;
+    }
+
+    err = at_tok_nextint(&p, &ct);
+    if (err) {
+        free(line);
+        return -1;
+    }
+    if (current) *current = ct;
+
+    ALOGD("line remaining after int: %s", p);
+
+    err = at_tok_nexthexint(&p, &pt);
+    if (err) {
+        free(line);
+        return 1;
+    }
+    if (preferred) {
+        *preferred = pt;
+    }
+    free(line);
+
+    return 0;
+}
+
+int query_supported_techs( ModemInfo *mdm, int *supported )
+{
+    ATResponse *p_response;
+    int err, val, techs = 0;
+    char *tok;
+    char *line;
+
+    ALOGD("query_supported_techs");
+    err = at_send_command_singleline("AT+CTEC=?", "+CTEC:", &p_response);
+    if (err || !p_response->success)
+        goto error;
+    line = p_response->p_intermediates->line;
+    err = at_tok_start(&line);
+    if (err || !at_tok_hasmore(&line))
+        goto error;
+    while (!at_tok_nextint(&line, &val)) {
+        techs |= ( 1 << val );
+    }
+    if (supported) *supported = techs;
+    return 0;
+error:
+    at_response_free(p_response);
+    return -1;
+}
+
+/**
+ * query_ctec. Send the +CTEC AT command to the modem to query the current
+ * and preferred modes. It leaves values in the addresses pointed to by
+ * current and preferred. If any of those pointers are NULL, the corresponding value
+ * is ignored, but the return value will still reflect if retreiving and parsing of the
+ * values suceeded.
+ *
+ * @mdm Currently unused
+ * @current A pointer to store the current mode returned by the modem. May be null.
+ * @preferred A pointer to store the preferred mode returned by the modem. May be null.
+ * @return -1 on error (or failure to parse)
+ *         1 if only the current mode was returned by modem (or failed to parse preferred)
+ *         0 if both current and preferred were returned correctly
+ */
+int query_ctec(ModemInfo *mdm, int *current, int32_t *preferred)
+{
+    ATResponse *response = NULL;
+    int err;
+    int res;
+
+    ALOGD("query_ctec. current: %d, preferred: %d", (int)current, (int) preferred);
+    err = at_send_command_singleline("AT+CTEC?", "+CTEC:", &response);
+    if (!err && response->success) {
+        res = parse_technology_response(response->p_intermediates->line, current, preferred);
+        at_response_free(response);
+        return res;
+    }
+    ALOGE("Error executing command: %d. response: %x. status: %d", err, (int)response, response? response->success : -1);
+    at_response_free(response);
+    return -1;
+}
+
+int is_multimode_modem(ModemInfo *mdm)
+{
+    ATResponse *response;
+    int err;
+    char *line;
+    int tech;
+    int32_t preferred;
+
+    if (query_ctec(mdm, &tech, &preferred) == 0) {
+        mdm->currentTech = tech;
+        mdm->preferredNetworkMode = preferred;
+        if (query_supported_techs(mdm, &mdm->supportedTechs)) {
+            return 0;
+        }
+        return 1;
+    }
+    return 0;
+}
+
+/**
+ * Find out if our modem is GSM, CDMA or both (Multimode)
+ */
+static void probeForModemMode(ModemInfo *info)
+{
+    ATResponse *response;
+    int err;
+    assert (info);
+    // Currently, our only known multimode modem is qemu's android modem,
+    // which implements the AT+CTEC command to query and set mode.
+    // Try that first
+
+    if (is_multimode_modem(info)) {
+        ALOGI("Found Multimode Modem. Supported techs mask: %8.8x. Current tech: %d",
+            info->supportedTechs, info->currentTech);
+        return;
+    }
+
+    /* Being here means that our modem is not multimode */
+    info->isMultimode = 0;
+
+    /* CDMA Modems implement the AT+WNAM command */
+    err = at_send_command_singleline("AT+WNAM","+WNAM:", &response);
+    if (!err && response->success) {
+        at_response_free(response);
+        // TODO: find out if we really support EvDo
+        info->supportedTechs = MDM_CDMA | MDM_EVDO;
+        info->currentTech = MDM_CDMA;
+        ALOGI("Found CDMA Modem");
+        return;
+    }
+    if (!err) at_response_free(response);
+    // TODO: find out if modem really supports WCDMA/LTE
+    info->supportedTechs = MDM_GSM | MDM_WCDMA | MDM_LTE;
+    info->currentTech = MDM_GSM;
+    ALOGI("Found GSM Modem");
+}
+
+/**
  * Initialize everything that can be configured while we're still in
  * AT+CFUN=0
  */
@@ -1869,6 +2782,7 @@ static void initializeCallback(void *param)
 
     at_handshake();
 
+    probeForModemMode(sMdmInfo);
     /* note: we don't check errors here. Everything important will
        be handled in onATTimeout and onATReaderClosed */
 
@@ -1934,7 +2848,7 @@ static void initializeCallback(void *param)
 
     /* assume radio is off on error */
     if (isRadioOn() > 0) {
-        setRadioState (RADIO_STATE_SIM_NOT_READY);
+        setRadioState (RADIO_STATE_ON);
     }
 }
 
@@ -1956,7 +2870,7 @@ static void waitForClose()
  */
 static void onUnsolicited (const char *s, const char *sms_pdu)
 {
-    char *line = NULL;
+    char *line = NULL, *p;
     int err;
 
     /* Ignore unsolicited responses until we're initialized.
@@ -1970,11 +2884,12 @@ static void onUnsolicited (const char *s, const char *sms_pdu)
         /* TI specific -- NITZ time */
         char *response;
 
-        line = strdup(s);
-        at_tok_start(&line);
+        line = p = strdup(s);
+        at_tok_start(&p);
 
-        err = at_tok_nextstr(&line, &response);
+        err = at_tok_nextstr(&p, &response);
 
+        free(line);
         if (err != 0) {
             ALOGE("invalid NITZ line %s\n", s);
         } else {
@@ -2021,6 +2936,88 @@ static void onUnsolicited (const char *s, const char *sms_pdu)
     } else if (strStartsWith(s, "+CME ERROR: 150")) {
         RIL_requestTimedCallback (onDataCallListChanged, NULL, NULL);
 #endif /* WORKAROUND_FAKE_CGEV */
+    } else if (strStartsWith(s, "+CTEC: ")) {
+        int tech, mask;
+        switch (parse_technology_response(s, &tech, NULL))
+        {
+            case -1: // no argument could be parsed.
+                ALOGE("invalid CTEC line %s\n", s);
+                break;
+            case 1: // current mode correctly parsed
+            case 0: // preferred mode correctly parsed
+                mask = 1 << tech;
+                if (mask != MDM_GSM && mask != MDM_CDMA &&
+                     mask != MDM_WCDMA && mask != MDM_LTE) {
+                    ALOGE("Unknown technology %d\n", tech);
+                } else {
+                    setRadioTechnology(sMdmInfo, tech);
+                }
+                break;
+        }
+    } else if (strStartsWith(s, "+CCSS: ")) {
+        int source = 0;
+        line = p = strdup(s);
+        if (!line) {
+            ALOGE("+CCSS: Unable to allocate memory");
+            return;
+        }
+        if (at_tok_start(&p) < 0) {
+            free(line);
+            return;
+        }
+        if (at_tok_nextint(&p, &source) < 0) {
+            ALOGE("invalid +CCSS response: %s", line);
+            free(line);
+            return;
+        }
+        SSOURCE(sMdmInfo) = source;
+        RIL_onUnsolicitedResponse(RIL_UNSOL_CDMA_SUBSCRIPTION_SOURCE_CHANGED,
+                                  &source, sizeof(source));
+    } else if (strStartsWith(s, "+WSOS: ")) {
+        char state = 0;
+        int unsol;
+        line = p = strdup(s);
+        if (!line) {
+            ALOGE("+WSOS: Unable to allocate memory");
+            return;
+        }
+        if (at_tok_start(&p) < 0) {
+            free(line);
+            return;
+        }
+        if (at_tok_nextbool(&p, &state) < 0) {
+            ALOGE("invalid +WSOS response: %s", line);
+            free(line);
+            return;
+        }
+        free(line);
+
+        unsol = state ?
+                RIL_UNSOL_ENTER_EMERGENCY_CALLBACK_MODE : RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE;
+
+        RIL_onUnsolicitedResponse(unsol, NULL, 0);
+
+    } else if (strStartsWith(s, "+WPRL: ")) {
+        int version = -1;
+        line = p = strdup(s);
+        if (!line) {
+            ALOGE("+WPRL: Unable to allocate memory");
+            return;
+        }
+        if (at_tok_start(&p) < 0) {
+            ALOGE("invalid +WPRL response: %s", s);
+            free(line);
+            return;
+        }
+        if (at_tok_nextint(&p, &version) < 0) {
+            ALOGE("invalid +WPRL response: %s", s);
+            free(line);
+            return;
+        }
+        free(line);
+        RIL_onUnsolicitedResponse(RIL_UNSOL_CDMA_PRL_CHANGED, &version, sizeof(version));
+    } else if (strStartsWith(s, "+CFUN: 0")) {
+        setRadioState(RADIO_STATE_OFF);
     }
 }
 
@@ -2184,6 +3181,11 @@ const RIL_RadioFunctions *RIL_Init(const struct RIL_Env *env, int argc, char **a
         return NULL;
     }
 
+    sMdmInfo = calloc(1, sizeof(ModemInfo));
+    if (!sMdmInfo) {
+        ALOGE("Unable to alloc memory for ModemInfo");
+        return NULL;
+    }
     pthread_attr_init (&attr);
     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
     ret = pthread_create(&s_tid_mainloop, &attr, mainLoop, NULL);