Add support for CellInfo RIL commands.

These commands allow for more precise location determination.

To support CellInfo I created librilutils which adds ril_nano_time
over time librilutils will be expanded to have other common methods
that would be used by ril implementations.

Bug: 8235566
Change-Id: I7ad7dabc4b6c38bfba4461b08e6e30d0eb9efea1
diff --git a/include/telephony/librilutils.h b/include/telephony/librilutils.h
new file mode 100644
index 0000000..d06b3e9
--- /dev/null
+++ b/include/telephony/librilutils.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LIBRILUTILS_H
+#define LIBRILUTILS_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Return system time in nanos.
+ *
+ * This is a monotonicly increasing clock and
+ * return the same value as System.nanoTime in java.
+ */
+uint64_t ril_nano_time();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // LIBRILUTILS_H
diff --git a/include/telephony/ril.h b/include/telephony/ril.h
index 76db7ba..a73343b 100644
--- a/include/telephony/ril.h
+++ b/include/telephony/ril.h
@@ -18,6 +18,7 @@
 #define ANDROID_RIL_H 1
 
 #include <stdlib.h>
+#include <stdint.h>
 #ifndef FEATURE_UNIT_TEST
 #include <sys/time.h>
 #endif /* !FEATURE_UNIT_TEST */
@@ -26,7 +27,7 @@
 extern "C" {
 #endif
 
-#define RIL_VERSION 7     /* Current version */
+#define RIL_VERSION 8     /* Current version */
 #define RIL_VERSION_MIN 6 /* Minimum RIL_VERSION supported */
 
 #define CDMA_ALPHA_INFO_BUFFER_LENGTH 64
@@ -208,7 +209,7 @@
                                            The unit is miliseconds.
                                            The value < 0 means no value is suggested.
                                            The value 0 means retry should be done ASAP.
-                                           The value of MAX_INT(0x7fffffff) means no retry. */
+                                           The value of INT_MAX(0x7fffffff) means no retry. */
     int             cid;        /* Context ID, uniquely identifies this call */
     int             active;     /* 0=inactive, 1=active/physical link down, 2=active/physical link up */
     char *          type;       /* One of the PDP_type values in TS 27.007 section 10.1.1.
@@ -681,6 +682,32 @@
                           * Reference: 3GPP TS 36.101 9.2, 9.3, A.4 */
 } RIL_LTE_SignalStrength;
 
+typedef struct {
+    int signalStrength;  /* Valid values are (0-31, 99) as defined in TS 27.007 8.5 */
+    int rsrp;            /* The current Reference Signal Receive Power in dBm multipled by -1.
+                          * Range: 44 to 140 dBm
+                          * INT_MAX: 0x7FFFFFFF denotes invalid value.
+                          * Reference: 3GPP TS 36.133 9.1.4 */
+    int rsrq;            /* The current Reference Signal Receive Quality in dB multiplied by -1.
+                          * Range: 20 to 3 dB.
+                          * INT_MAX: 0x7FFFFFFF denotes invalid value.
+                          * Reference: 3GPP TS 36.133 9.1.7 */
+    int rssnr;           /* The current reference signal signal-to-noise ratio in 0.1 dB units.
+                          * Range: -200 to +300 (-200 = -20.0 dB, +300 = 30dB).
+                          * INT_MAX : 0x7FFFFFFF denotes invalid value.
+                          * Reference: 3GPP TS 36.101 8.1.1 */
+    int cqi;             /* The current Channel Quality Indicator.
+                          * Range: 0 to 15.
+                          * INT_MAX : 0x7FFFFFFF denotes invalid value.
+                          * Reference: 3GPP TS 36.101 9.2, 9.3, A.4 */
+    int timingAdvance;   /* timing advance in micro seconds for a one way trip from cell to device.
+                          * Approximate distance can be calculated using 300m/us * timingAdvance.
+                          * Range: 0 to 0x7FFFFFFE
+                          * INT_MAX : 0x7FFFFFFF denotes invalid value.
+                          * Reference: 3GPP 36.321 section 6.1.3.5
+                          * also: http://www.cellular-planningoptimization.com/2010/02/timing-advance-with-calculation.html */
+} RIL_LTE_SignalStrength_v8;
+
 /* Deprecated, use RIL_SignalStrength_v6 */
 typedef struct {
     RIL_GW_SignalStrength   GW_SignalStrength;
@@ -695,6 +722,95 @@
     RIL_LTE_SignalStrength  LTE_SignalStrength;
 } RIL_SignalStrength_v6;
 
+typedef struct {
+    RIL_GW_SignalStrength       GW_SignalStrength;
+    RIL_CDMA_SignalStrength     CDMA_SignalStrength;
+    RIL_EVDO_SignalStrength     EVDO_SignalStrength;
+    RIL_LTE_SignalStrength_v8   LTE_SignalStrength;
+} RIL_SignalStrength_v8;
+
+/** RIL_CellIdentityGsm */
+typedef struct {
+    int mcc;    /* 3-digit Mobile Country Code, 0..999 */
+    int mnc;    /* 2 or 3-digit Mobile Network Code, 0..999 */
+    int lac;    /* 16-bit Location Area Code, 0..65535 */
+    int cid;    /* 16-bit GSM Cell Identity described in TS 27.007, 0..65535
+                 * 28-bit UMTS Cell Identity described in TS 25.331, 0..268435455 */
+    int psc;    /* 9-bit UMTS Primary Scrambling Code described in TS 25.331, 0..511 */
+} RIL_CellIdentityGsm;
+
+/** RIL_CellIdentityCdma */
+typedef struct {
+    int networkId;      /* Network Id 0..65535 */
+    int systemId;       /* CDMA System Id 0..32767 */
+    int basestationId;  /* Base Station Id 0..65535 */
+    int longitude;      /* Longitude is a decimal number as specified in 3GPP2 C.S0005-A v6.0.
+                         * It is represented in units of 0.25 seconds and ranges from -2592000
+                         * to 2592000, both values inclusive (corresponding to a range of -180
+                         * to +180 degrees). */
+
+    int latitude;       /* Latitude is a decimal number as specified in 3GPP2 C.S0005-A v6.0.
+                         * It is represented in units of 0.25 seconds and ranges from -1296000
+                         * to 1296000, both values inclusive (corresponding to a range of -90
+                         * to +90 degrees). */
+} RIL_CellIdentityCdma;
+
+/** RIL_CellIdentityLte */
+typedef struct {
+    int mcc;    /* 3-digit Mobile Country Code, 0..999 */
+    int mnc;    /* 2 or 3-digit Mobile Network Code, 0..999 */
+    int ci;     /* 28-bit Cell Identity described in TS ??? */
+    int pci;    /* physical cell id 0..503 */
+    int tac;    /* 16-bit tracking area code */
+} RIL_CellIdentityLte;
+
+/** RIL_CellInfoGsm */
+typedef struct {
+  RIL_CellIdentityGsm   cellIdentityGsm;
+  RIL_GW_SignalStrength signalStrengthGsm;
+} RIL_CellInfoGsm;
+
+/** RIL_CellInfoCdma */
+typedef struct {
+  RIL_CellIdentityCdma      cellIdentityCdma;
+  RIL_CDMA_SignalStrength   signalStrengthCdma;
+  RIL_EVDO_SignalStrength   signalStrengthEvdo;
+} RIL_CellInfoCdma;
+
+/** RIL_CellInfoLte */
+typedef struct {
+  RIL_CellIdentityLte        cellIdentityLte;
+  RIL_LTE_SignalStrength_v8  signalStrengthLte;
+} RIL_CellInfoLte;
+
+// Must be the same as CellInfo.TYPE_XXX
+typedef enum {
+  RIL_CELL_INFO_TYPE_GSM    = 1,
+  RIL_CELL_INFO_TYPE_CDMA   = 2,
+  RIL_CELL_INFO_TYPE_LTE    = 3,
+} RIL_CellInfoType;
+
+// Must be the same as CellInfo.TIMESTAMP_TYPE_XXX
+typedef enum {
+    RIL_TIMESTAMP_TYPE_UNKNOWN = 0,
+    RIL_TIMESTAMP_TYPE_ANTENNA = 1,
+    RIL_TIMESTAMP_TYPE_MODEM = 2,
+    RIL_TIMESTAMP_TYPE_OEM_RIL = 3,
+    RIL_TIMESTAMP_TYPE_JAVA_RIL = 4,
+} RIL_TimeStampType;
+
+typedef struct {
+  RIL_CellInfoType  cellInfoType;   /* cell type for selecting from union CellInfo */
+  int               registered;     /* !0 if this cell is registered 0 if not registered */
+  RIL_TimeStampType timeStampType;  /* type of time stamp represented by timeStamp */
+  uint64_t          timeStamp;      /* Time in nanos as returned by ril_nano_time */
+  union {
+    RIL_CellInfoGsm     gsm;
+    RIL_CellInfoCdma    cdma;
+    RIL_CellInfoLte     lte;
+  } CellInfo;
+} RIL_CellInfo;
+
 /* Names of the CDMA info records (C.S0005 section 3.7.5) */
 typedef enum {
   RIL_CDMA_DISPLAY_INFO_REC,
@@ -3313,6 +3429,39 @@
  */
 #define RIL_REQUEST_VOICE_RADIO_TECH 108
 
+/**
+ * RIL_REQUEST_GET_CELL_INFO_LIST
+ *
+ * Request all of the current cell information known to the radio. The radio
+ * must a list of all current cells, including the neighboring cells. If for a particular
+ * cell information isn't known then the appropriate unknown value will be returned.
+ * This does not cause or change the rate of RIL_UNSOL_CELL_INFO_LIST.
+ *
+ * "data" is NULL
+ *
+ * "response" is an array of  RIL_CellInfo.
+ */
+#define RIL_REQUEST_GET_CELL_INFO_LIST 109
+
+/**
+ * RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE
+ *
+ * Sets the minimum time between when RIL_UNSOL_CELL_INFO_LIST should be invoked.
+ * The default, 0, means invoke RIL_UNSOL_CELL_INFO_LIST when any of the reported
+ * information changes. Setting the value to INT_MAX(0x7fffffff) means never issue
+ * a RIL_UNSOL_CELL_INFO_LIST.
+ *
+ * "data" is int *
+ * ((int *)data)[0] is minimum time in milliseconds
+ *
+ * "response" is NULL
+ *
+ * Valid errors:
+ *  SUCCESS
+ *  RADIO_NOT_AVAILABLE
+ *  GENERIC_FAILURE
+ */
+#define RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE 110
 
 /***********************************************************************/
 
@@ -3792,6 +3941,17 @@
  */
 #define RIL_UNSOL_VOICE_RADIO_TECH_CHANGED 1035
 
+/**
+ * RIL_UNSOL_CELL_INFO_LIST
+ *
+ * Same information as returned by RIL_REQUEST_GET_CELL_INFO_LIST, but returned
+ * at the rate no greater than specified by RIL_REQUEST_SET_UNSOL_CELL_INFO_RATE.
+ *
+ * "data" is NULL
+ *
+ * "response" is an array of RIL_CellInfo.
+ */
+#define RIL_UNSOL_CELL_INFO_LIST 1036
 
 /***********************************************************************/
 
diff --git a/libril/ril.cpp b/libril/ril.cpp
index 7155de1..4d96988 100644
--- a/libril/ril.cpp
+++ b/libril/ril.cpp
@@ -234,6 +234,7 @@
 static int responseCdmaSignalInfoRecord(Parcel &p,void *response, size_t responselen);
 static int responseCdmaCallWaiting(Parcel &p,void *response, size_t responselen);
 static int responseSimRefresh(Parcel &p, void *response, size_t responselen);
+static int responseCellInfoList(Parcel &p, void *response, size_t responselen);
 
 static int decodeVoiceRadioTechnology (RIL_RadioState radioState);
 static int decodeCdmaSubscriptionSource (RIL_RadioState radioState);
@@ -2185,6 +2186,119 @@
     return 0;
 }
 
+static int responseCellInfoList(Parcel &p, void *response, size_t responselen)
+{
+    if (response == NULL && responselen != 0) {
+        RLOGE("invalid response: NULL");
+        return RIL_ERRNO_INVALID_RESPONSE;
+    }
+
+    if (responselen % sizeof(RIL_CellInfo) != 0) {
+        RLOGE("invalid response length %d expected multiple of %d",
+                (int)responselen, (int)sizeof(RIL_CellInfo));
+        return RIL_ERRNO_INVALID_RESPONSE;
+    }
+
+    int num = responselen / sizeof(RIL_CellInfo);
+    p.writeInt32(num);
+
+    RIL_CellInfo *p_cur = (RIL_CellInfo *) response;
+    startResponse;
+    int i;
+    for (i = 0; i < num; i++) {
+        appendPrintBuf("%s[%d: type=%d,registered=%d,timeStampType=%d,timeStamp=%lld", printBuf, i,
+            p_cur->cellInfoType, p_cur->registered, p_cur->timeStampType, p_cur->timeStamp);
+        p.writeInt32((int)p_cur->cellInfoType);
+        p.writeInt32(p_cur->registered);
+        p.writeInt32(p_cur->timeStampType);
+        p.writeInt64(p_cur->timeStamp);
+        switch(p_cur->cellInfoType) {
+            case RIL_CELL_INFO_TYPE_GSM: {
+                appendPrintBuf("%s GSM id: mcc=%d,mnc=%d,lac=%d,cid=%d,psc=%d,", printBuf,
+                    p_cur->CellInfo.gsm.cellIdentityGsm.mcc,
+                    p_cur->CellInfo.gsm.cellIdentityGsm.mnc,
+                    p_cur->CellInfo.gsm.cellIdentityGsm.lac,
+                    p_cur->CellInfo.gsm.cellIdentityGsm.cid,
+                    p_cur->CellInfo.gsm.cellIdentityGsm.psc);
+                appendPrintBuf("%s SS: gsmSS ss=%d,ber=%d],", printBuf,
+                    p_cur->CellInfo.gsm.signalStrengthGsm.signalStrength,
+                    p_cur->CellInfo.gsm.signalStrengthGsm.bitErrorRate);
+
+                p.writeInt32(p_cur->CellInfo.gsm.cellIdentityGsm.mcc);
+                p.writeInt32(p_cur->CellInfo.gsm.cellIdentityGsm.mnc);
+                p.writeInt32(p_cur->CellInfo.gsm.cellIdentityGsm.lac);
+                p.writeInt32(p_cur->CellInfo.gsm.cellIdentityGsm.cid);
+                p.writeInt32(p_cur->CellInfo.gsm.cellIdentityGsm.psc);
+                p.writeInt32(p_cur->CellInfo.gsm.signalStrengthGsm.signalStrength);
+                p.writeInt32(p_cur->CellInfo.gsm.signalStrengthGsm.bitErrorRate);
+                break;
+            }
+            case RIL_CELL_INFO_TYPE_CDMA: {
+                appendPrintBuf("%s CDMA id: nId=%d,sId=%d,bsId=%d,long=%d,lat=%d", printBuf,
+                    p_cur->CellInfo.cdma.cellIdentityCdma.networkId,
+                    p_cur->CellInfo.cdma.cellIdentityCdma.systemId,
+                    p_cur->CellInfo.cdma.cellIdentityCdma.basestationId,
+                    p_cur->CellInfo.cdma.cellIdentityCdma.longitude,
+                    p_cur->CellInfo.cdma.cellIdentityCdma.latitude);
+
+                p.writeInt32(p_cur->CellInfo.cdma.cellIdentityCdma.networkId);
+                p.writeInt32(p_cur->CellInfo.cdma.cellIdentityCdma.systemId);
+                p.writeInt32(p_cur->CellInfo.cdma.cellIdentityCdma.basestationId);
+                p.writeInt32(p_cur->CellInfo.cdma.cellIdentityCdma.longitude);
+                p.writeInt32(p_cur->CellInfo.cdma.cellIdentityCdma.latitude);
+
+                appendPrintBuf("%s cdmaSS: dbm=%d ecio=%d evdoSS: dbm=%d,ecio=%d,snr=%d", printBuf,
+                    p_cur->CellInfo.cdma.signalStrengthCdma.dbm,
+                    p_cur->CellInfo.cdma.signalStrengthCdma.ecio,
+                    p_cur->CellInfo.cdma.signalStrengthEvdo.dbm,
+                    p_cur->CellInfo.cdma.signalStrengthEvdo.ecio,
+                    p_cur->CellInfo.cdma.signalStrengthEvdo.signalNoiseRatio);
+
+                p.writeInt32(p_cur->CellInfo.cdma.signalStrengthCdma.dbm);
+                p.writeInt32(p_cur->CellInfo.cdma.signalStrengthCdma.ecio);
+                p.writeInt32(p_cur->CellInfo.cdma.signalStrengthEvdo.dbm);
+                p.writeInt32(p_cur->CellInfo.cdma.signalStrengthEvdo.ecio);
+                p.writeInt32(p_cur->CellInfo.cdma.signalStrengthEvdo.signalNoiseRatio);
+                break;
+            }
+            case RIL_CELL_INFO_TYPE_LTE: {
+                appendPrintBuf("%s LTE id: mcc=%d,mnc=%d,ci=%d,pci=%d,tac=%d", printBuf,
+                    p_cur->CellInfo.lte.cellIdentityLte.mcc,
+                    p_cur->CellInfo.lte.cellIdentityLte.mnc,
+                    p_cur->CellInfo.lte.cellIdentityLte.ci,
+                    p_cur->CellInfo.lte.cellIdentityLte.pci,
+                    p_cur->CellInfo.lte.cellIdentityLte.tac);
+
+                p.writeInt32(p_cur->CellInfo.lte.cellIdentityLte.mcc);
+                p.writeInt32(p_cur->CellInfo.lte.cellIdentityLte.mnc);
+                p.writeInt32(p_cur->CellInfo.lte.cellIdentityLte.ci);
+                p.writeInt32(p_cur->CellInfo.lte.cellIdentityLte.pci);
+                p.writeInt32(p_cur->CellInfo.lte.cellIdentityLte.tac);
+
+                appendPrintBuf("%s lteSS: ss=%d,rsrp=%d,rsrq=%d,rssnr=%d,cqi=%d,ta=%d", printBuf,
+                    p_cur->CellInfo.lte.signalStrengthLte.signalStrength,
+                    p_cur->CellInfo.lte.signalStrengthLte.rsrp,
+                    p_cur->CellInfo.lte.signalStrengthLte.rsrq,
+                    p_cur->CellInfo.lte.signalStrengthLte.rssnr,
+                    p_cur->CellInfo.lte.signalStrengthLte.cqi,
+                    p_cur->CellInfo.lte.signalStrengthLte.timingAdvance);
+                p.writeInt32(p_cur->CellInfo.lte.signalStrengthLte.signalStrength);
+                p.writeInt32(p_cur->CellInfo.lte.signalStrengthLte.rsrp);
+                p.writeInt32(p_cur->CellInfo.lte.signalStrengthLte.rsrq);
+                p.writeInt32(p_cur->CellInfo.lte.signalStrengthLte.rssnr);
+                p.writeInt32(p_cur->CellInfo.lte.signalStrengthLte.cqi);
+                p.writeInt32(p_cur->CellInfo.lte.signalStrengthLte.timingAdvance);
+                break;
+            }
+        }
+        p_cur += 1;
+    }
+    removeLastChar;
+    closeResponse;
+
+    return 0;
+}
+
 static void triggerEvLoop() {
     int ret;
     if (!pthread_equal(pthread_self(), s_tid_dispatch)) {
@@ -3467,6 +3581,8 @@
         case RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU: return "RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU";
         case RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS: return "RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS";
         case RIL_REQUEST_VOICE_RADIO_TECH: return "VOICE_RADIO_TECH";
+        case RIL_REQUEST_GET_CELL_INFO_LIST: return"GET_CELL_INFO_LIST";
+        case RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE: return"SET_UNSOL_CELL_INFO_LIST_RATE";
         case RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED: return "UNSOL_RESPONSE_RADIO_STATE_CHANGED";
         case RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED: return "UNSOL_RESPONSE_CALL_STATE_CHANGED";
         case RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED: return "UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED";
@@ -3502,6 +3618,7 @@
         case RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE: return "UNSOL_EXIT_EMERGENCY_CALLBACK_MODE";
         case RIL_UNSOL_RIL_CONNECTED: return "UNSOL_RIL_CONNECTED";
         case RIL_UNSOL_VOICE_RADIO_TECH_CHANGED: return "UNSOL_VOICE_RADIO_TECH_CHANGED";
+        case RIL_UNSOL_CELL_INFO_LIST: return "UNSOL_CELL_INFO_LIST";
         default: return "<unknown request>";
     }
 }
diff --git a/libril/ril_commands.h b/libril/ril_commands.h
index 069ee75..0519a2a 100644
--- a/libril/ril_commands.h
+++ b/libril/ril_commands.h
@@ -123,3 +123,5 @@
     {RIL_REQUEST_ACKNOWLEDGE_INCOMING_GSM_SMS_WITH_PDU, dispatchStrings, responseVoid},
     {RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS, dispatchString, responseSIM_IO},
     {RIL_REQUEST_VOICE_RADIO_TECH, dispatchVoiceRadioTech, responseInts},
+    {RIL_REQUEST_GET_CELL_INFO_LIST, dispatchVoid, responseCellInfoList},
+    {RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE, dispatchInts, responseVoid},
diff --git a/libril/ril_unsol_commands.h b/libril/ril_unsol_commands.h
index 7e3f8f0..0711225 100644
--- a/libril/ril_unsol_commands.h
+++ b/libril/ril_unsol_commands.h
@@ -50,3 +50,4 @@
     {RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE, responseVoid, WAKE_PARTIAL},
     {RIL_UNSOL_RIL_CONNECTED, responseInts, WAKE_PARTIAL},
     {RIL_UNSOL_VOICE_RADIO_TECH_CHANGED, responseInts, WAKE_PARTIAL},
+    {RIL_UNSOL_CELL_INFO_LIST, responseCellInfoList, WAKE_PARTIAL},
diff --git a/librilutils/Android.mk b/librilutils/Android.mk
new file mode 100644
index 0000000..a959202
--- /dev/null
+++ b/librilutils/Android.mk
@@ -0,0 +1,33 @@
+# Copyright 2013 The Android Open Source Project
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+    librilutils.c
+
+LOCAL_CFLAGS :=
+
+LOCAL_MODULE:= librilutils
+
+LOCAL_LDLIBS += -lpthread
+
+include $(BUILD_SHARED_LIBRARY)
+
+
+# Create static library for those that want it
+# =========================================
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+    librilutils.c
+
+LOCAL_STATIC_LIBRARIES :=
+
+LOCAL_CFLAGS :=
+
+LOCAL_MODULE:= librilutils_static
+
+LOCAL_LDLIBS += -lpthread
+
+include $(BUILD_STATIC_LIBRARY)
diff --git a/librilutils/librilutils.c b/librilutils/librilutils.c
new file mode 100644
index 0000000..b1b930e
--- /dev/null
+++ b/librilutils/librilutils.c
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <telephony/librilutils.h>
+#include <time.h>
+
+uint64_t ril_nano_time() {
+    struct timespec now;
+    clock_gettime(CLOCK_MONOTONIC, &now);
+    return now.tv_sec * 1000000000LL + now.tv_nsec;
+}
diff --git a/reference-ril/Android.mk b/reference-ril/Android.mk
index ec2692e..85d1c17 100644
--- a/reference-ril/Android.mk
+++ b/reference-ril/Android.mk
@@ -12,7 +12,7 @@
     at_tok.c
 
 LOCAL_SHARED_LIBRARIES := \
-    libcutils libutils libril
+    libcutils libutils libril librilutils
 
 # for asprinf
 LOCAL_CFLAGS := -D_GNU_SOURCE
diff --git a/reference-ril/reference-ril.c b/reference-ril/reference-ril.c
index d1dd23f..facd223 100644
--- a/reference-ril/reference-ril.c
+++ b/reference-ril/reference-ril.c
@@ -16,6 +16,7 @@
 */
 
 #include <telephony/ril_cdma_sms.h>
+#include <telephony/librilutils.h>
 #include <stdio.h>
 #include <assert.h>
 #include <string.h>
@@ -223,6 +224,12 @@
 static int s_expectAnswer = 0;
 #endif /* WORKAROUND_ERRONEOUS_ANSWER */
 
+static int s_cell_info_rate_ms = INT_MAX;
+static int s_mcc = 0;
+static int s_mnc = 0;
+static int s_lac = 0;
+static int s_cid = 0;
+
 static void pollSIMState (void *param);
 static void setRadioState(RIL_RadioState newState);
 static void setRadioTechnology(ModemInfo *mdm, int newtech);
@@ -1261,6 +1268,8 @@
         default:
             goto error;
     }
+    s_lac = resp[1];
+    s_cid = resp[2];
     if (response)
         *response = resp;
     if (items)
@@ -1443,6 +1452,12 @@
 
         err = at_tok_nextstr(&line, &(response[i]));
         if (err < 0) goto error;
+        // Simple assumption that mcc and mnc are 3 digits each
+        if (strlen(response[i]) == 6) {
+            if (sscanf(response[i], "%3d%3d", &s_mcc, &s_mnc) != 2) {
+                RLOGE("requestOperator expected mccmnc to be 6 decimal digits");
+            }
+        }
     }
 
     if (i != 3) {
@@ -1815,6 +1830,47 @@
     return ret;
 }
 
+static void requestGetCellInfoList(void *data, size_t datalen, RIL_Token t)
+{
+    uint64_t curTime = ril_nano_time();
+    RIL_CellInfo ci[1] =
+    {
+        { // ci[0]
+            1, // cellInfoType
+            1, // registered
+            curTime - 1000, // Fake some time in the past
+            { // union CellInfo
+                {  // RIL_CellInfoGsm gsm
+                    {  // gsm.cellIdneityGsm
+                        s_mcc, // mcc
+                        s_mnc, // mnc
+                        s_lac, // lac
+                        s_cid, // cid
+                        0  // psc
+                    },
+                    {  // gsm.signalStrengthGsm
+                        10, // signalStrength
+                        0  // bitErrorRate
+                    }
+                }
+            }
+        }
+    };
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, ci, sizeof(ci));
+}
+
+
+static void requestSetCellInfoListRate(void *data, size_t datalen, RIL_Token t)
+{
+    // For now we'll save the rate but no RIL_UNSOL_CELL_INFO_LIST messages
+    // will be sent.
+    assert (datalen == sizeof(int));
+    s_cell_info_rate_ms = ((int *)data)[0];
+
+    RIL_onRequestComplete(t, RIL_E_SUCCESS, NULL, 0);
+}
+
 /*** Callback methods from the RIL library to us ***/
 
 /**
@@ -2178,6 +2234,14 @@
                 break;
             } // Fall-through if tech is not cdma
 
+        case RIL_REQUEST_GET_CELL_INFO_LIST:
+            requestGetCellInfoList(data, datalen, t);
+            break;
+
+        case RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE:
+            requestSetCellInfoListRate(data, datalen, t);
+            break;
+
         default:
             RLOGD("Request not supported. Tech: %d",TECH(sMdmInfo));
             RIL_onRequestComplete(t, RIL_E_REQUEST_NOT_SUPPORTED, NULL, 0);