gps: Add support for bringing up a GSM data connection on demand for SUPL.

Signed-off-by: Mike Lockwood <lockwood@android.com>
diff --git a/core/jni/android_location_GpsLocationProvider.cpp b/core/jni/android_location_GpsLocationProvider.cpp
index f0b35e9..bbde8d5 100644
--- a/core/jni/android_location_GpsLocationProvider.cpp
+++ b/core/jni/android_location_GpsLocationProvider.cpp
@@ -31,6 +31,7 @@
 static jmethodID method_reportLocation;
 static jmethodID method_reportStatus;
 static jmethodID method_reportSvStatus;
+static jmethodID method_reportSuplStatus;
 static jmethodID method_xtraDownloadRequest;
 
 static const GpsInterface* sGpsInterface = NULL;
@@ -41,19 +42,22 @@
 static GpsLocation  sGpsLocation;
 static GpsStatus    sGpsStatus;
 static GpsSvStatus  sGpsSvStatus;
+static GpsSuplStatus    sGpsSuplStatus;
 
 // a copy of the data shared by android_location_GpsLocationProvider_wait_for_event
 // and android_location_GpsLocationProvider_read_status
 static GpsLocation  sGpsLocationCopy;
 static GpsStatus    sGpsStatusCopy;
 static GpsSvStatus  sGpsSvStatusCopy;
+static GpsSuplStatus    sGpsSuplStatusCopy;
 
 enum CallbackType {
     kLocation = 1,
     kStatus = 2,
     kSvStatus = 4,
-    kXtraDownloadRequest = 8,
-    kDisableRequest = 16,
+    kSuplStatus = 8,
+    kXtraDownloadRequest = 16,
+    kDisableRequest = 32,
 }; 
 static int sPendingCallbacks;
 
@@ -92,6 +96,17 @@
     pthread_mutex_unlock(&sEventMutex);
 }
 
+static void supl_status_callback(GpsSuplStatus* supl_status)
+{
+    pthread_mutex_lock(&sEventMutex);
+
+    sPendingCallbacks |= kSuplStatus;
+    memcpy(&sGpsSuplStatus, supl_status, sizeof(GpsSuplStatus));
+
+    pthread_cond_signal(&sEventCond);
+    pthread_mutex_unlock(&sEventMutex);
+}
+
 GpsCallbacks sGpsCallbacks = {
     location_callback,
     status_callback,
@@ -111,11 +126,15 @@
     download_request_callback,
 };
 
+GpsSuplCallbacks sGpsSuplCallbacks = {
+    supl_status_callback,
+};
 
 static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {
     method_reportLocation = env->GetMethodID(clazz, "reportLocation", "(IDDDFFFJ)V");
     method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V");
     method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "()V");
+    method_reportSuplStatus = env->GetMethodID(clazz, "reportSuplStatus", "(I)V");
     method_xtraDownloadRequest = env->GetMethodID(clazz, "xtraDownloadRequest", "()V");
 }
 
@@ -129,7 +148,13 @@
 {
     if (!sGpsInterface)
         sGpsInterface = gps_get_interface();
-    return (sGpsInterface && sGpsInterface->init(&sGpsCallbacks) == 0);
+    if (!sGpsInterface || sGpsInterface->init(&sGpsCallbacks) != 0)
+        return false;
+
+    if (!sGpsSuplInterface)
+        sGpsSuplInterface = (const GpsSuplInterface*)sGpsInterface->get_extension(GPS_SUPL_INTERFACE);
+    if (sGpsSuplInterface)
+        sGpsSuplInterface->init(&sGpsSuplCallbacks);
 }
 
 static void android_location_GpsLocationProvider_disable(JNIEnv* env, jobject obj)
@@ -186,6 +211,7 @@
     memcpy(&sGpsLocationCopy, &sGpsLocation, sizeof(sGpsLocationCopy));
     memcpy(&sGpsStatusCopy, &sGpsStatus, sizeof(sGpsStatusCopy));
     memcpy(&sGpsSvStatusCopy, &sGpsSvStatus, sizeof(sGpsSvStatusCopy));
+    memcpy(&sGpsSuplStatusCopy, &sGpsSuplStatus, sizeof(sGpsSuplStatusCopy));
     pthread_mutex_unlock(&sEventMutex);   
 
     if (pendingCallbacks & kLocation) { 
@@ -201,6 +227,9 @@
     if (pendingCallbacks & kSvStatus) {
         env->CallVoidMethod(obj, method_reportSvStatus);
     }
+    if (pendingCallbacks & kSuplStatus) {
+        env->CallVoidMethod(obj, method_reportSuplStatus, sGpsSuplStatusCopy.status);
+    }  
     if (pendingCallbacks & kXtraDownloadRequest) {    
         env->CallVoidMethod(obj, method_xtraDownloadRequest);
     }
@@ -269,18 +298,7 @@
     env->ReleaseByteArrayElements(data, bytes, 0);
 }
 
-static void android_location_GpsLocationProvider_set_supl_server(JNIEnv* env, jobject obj,
-        jint addr, jint port)
-{
-    if (!sGpsSuplInterface) {
-        sGpsSuplInterface = (const GpsSuplInterface*)sGpsInterface->get_extension(GPS_SUPL_INTERFACE);
-    }
-    if (sGpsSuplInterface) {
-        sGpsSuplInterface->set_server(addr, port);
-    }
-}
-
-static void android_location_GpsLocationProvider_set_supl_apn(JNIEnv* env, jobject obj, jstring apn)
+static void android_location_GpsLocationProvider_supl_data_conn_open(JNIEnv* env, jobject obj, jstring apn)
 {
     if (!sGpsSuplInterface) {
         sGpsSuplInterface = (const GpsSuplInterface*)sGpsInterface->get_extension(GPS_SUPL_INTERFACE);
@@ -291,11 +309,42 @@
             return;
         }
         const char *apnStr = env->GetStringUTFChars(apn, NULL);
-        sGpsSuplInterface->set_apn(apnStr);
+        sGpsSuplInterface->data_conn_open(apnStr);
         env->ReleaseStringUTFChars(apn, apnStr);
     }
 }
 
+static void android_location_GpsLocationProvider_supl_data_conn_closed(JNIEnv* env, jobject obj)
+{
+    if (!sGpsSuplInterface) {
+        sGpsSuplInterface = (const GpsSuplInterface*)sGpsInterface->get_extension(GPS_SUPL_INTERFACE);
+    }
+    if (sGpsSuplInterface) {
+        sGpsSuplInterface->data_conn_closed();
+    }
+}
+
+static void android_location_GpsLocationProvider_supl_data_conn_failed(JNIEnv* env, jobject obj)
+{
+    if (!sGpsSuplInterface) {
+        sGpsSuplInterface = (const GpsSuplInterface*)sGpsInterface->get_extension(GPS_SUPL_INTERFACE);
+    }
+    if (sGpsSuplInterface) {
+        sGpsSuplInterface->data_conn_failed();
+    }
+}
+
+static void android_location_GpsLocationProvider_set_supl_server(JNIEnv* env, jobject obj,
+        jint addr, jint port)
+{
+    if (!sGpsSuplInterface) {
+        sGpsSuplInterface = (const GpsSuplInterface*)sGpsInterface->get_extension(GPS_SUPL_INTERFACE);
+    }
+    if (sGpsSuplInterface) {
+        sGpsSuplInterface->set_server(addr, port);
+    }
+}
+
 static JNINativeMethod sMethods[] = {
      /* name, signature, funcPtr */
     {"class_init_native", "()V", (void *)android_location_GpsLocationProvider_class_init_native},
@@ -312,8 +361,10 @@
 	{"native_inject_time", "(JJI)V", (void*)android_location_GpsLocationProvider_inject_time},
 	{"native_supports_xtra", "()Z", (void*)android_location_GpsLocationProvider_supports_xtra},
 	{"native_inject_xtra_data", "([BI)V", (void*)android_location_GpsLocationProvider_inject_xtra_data},
+ 	{"native_supl_data_conn_open", "(Ljava/lang/String;)V", (void*)android_location_GpsLocationProvider_supl_data_conn_open},
+ 	{"native_supl_data_conn_closed", "()V", (void*)android_location_GpsLocationProvider_supl_data_conn_closed},
+ 	{"native_supl_data_conn_failed", "()V", (void*)android_location_GpsLocationProvider_supl_data_conn_failed},
  	{"native_set_supl_server", "(II)V", (void*)android_location_GpsLocationProvider_set_supl_server},
- 	{"native_set_supl_apn", "(Ljava/lang/String;)V", (void*)android_location_GpsLocationProvider_set_supl_apn},
 };
 
 int register_android_location_GpsLocationProvider(JNIEnv* env)
diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java
index 8a33574..d56594e 100644
--- a/location/java/com/android/internal/location/GpsLocationProvider.java
+++ b/location/java/com/android/internal/location/GpsLocationProvider.java
@@ -28,6 +28,7 @@
 import android.location.LocationManager;
 import android.location.LocationProvider;
 import android.location.LocationProviderImpl;
+import android.net.ConnectivityManager;
 import android.net.SntpClient;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -99,6 +100,14 @@
     private static final int GPS_STATUS_ENGINE_ON = 3;
     private static final int GPS_STATUS_ENGINE_OFF = 4;
 
+    // these need to match GpsSuplStatusValue defines in gps.h
+    /** SUPL status event values. */
+    private static final int GPS_REQUEST_SUPL_DATA_CONN = 1;
+    private static final int GPS_RELEASE_SUPL_DATA_CONN = 2;
+    private static final int GPS_SUPL_DATA_CONNECTED = 3;
+    private static final int GPS_SUPL_DATA_CONN_DONE = 4;
+    private static final int GPS_SUPL_DATA_CONN_FAILED = 5;
+
     // these need to match GpsLocationFlags enum in gps.h
     private static final int LOCATION_INVALID = 0;
     private static final int LOCATION_HAS_LAT_LONG = 1;
@@ -122,6 +131,11 @@
     private static final int GPS_DELETE_CELLDB_INFO = 0x8000;
     private static final int GPS_DELETE_ALL = 0xFFFF;
 
+    // for mSuplDataConnectionState
+    private static final int SUPL_DATA_CONNECTION_CLOSED = 0;
+    private static final int SUPL_DATA_CONNECTION_OPENING = 1;
+    private static final int SUPL_DATA_CONNECTION_OPEN = 2;
+
     private static final String PROPERTIES_FILE = "/etc/gps.conf";
 
     private int mLocationFlags = LOCATION_INVALID;
@@ -176,6 +190,9 @@
     private String mSuplHost;
     private int mSuplPort;
     private boolean mSetSuplServer;
+    private String mSuplApn;
+    private int mSuplDataConnectionState;
+    private ConnectivityManager mConnMgr;
 
     // how often to request NTP time, in milliseconds
     // current setting 4 hours
@@ -199,9 +216,11 @@
                     Log.d(TAG, "state: " + state +  " apnName: " + apnName + " reason: " + reason);
                 }
                 if ("CONNECTED".equals(state)) {
-                    native_set_supl_apn(apnName);
-                } else {
-                    native_set_supl_apn("");
+                    mSuplApn = apnName;
+                    if (mSuplDataConnectionState == SUPL_DATA_CONNECTION_OPENING) {
+                        native_supl_data_conn_open(mSuplApn);
+                        mSuplDataConnectionState = SUPL_DATA_CONNECTION_OPEN;
+                    }
                 }
             }
         }
@@ -220,6 +239,8 @@
         intentFilter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
         context.registerReceiver(receiver, intentFilter);
 
+        mConnMgr = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
+
         mProperties = new Properties();
         try {
             File file = new File(PROPERTIES_FILE);
@@ -782,7 +803,44 @@
             updateStatus(TEMPORARILY_UNAVAILABLE, mSvCount);
         }
     }
-    
+
+    /**
+     * called from native code to update SUPL status
+     */
+    private void reportSuplStatus(int status) {
+        switch (status) {
+            case GPS_REQUEST_SUPL_DATA_CONN:
+                 int result = mConnMgr.startUsingNetworkFeature(
+                        ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL);
+                if (result == Phone.APN_ALREADY_ACTIVE) {
+                    native_supl_data_conn_open(mSuplApn);
+                    mSuplDataConnectionState = SUPL_DATA_CONNECTION_OPEN;
+                } else if (result == Phone.APN_REQUEST_STARTED) {
+                    mSuplDataConnectionState = SUPL_DATA_CONNECTION_OPENING;
+                } else {
+                    native_supl_data_conn_failed();
+                }
+                break;
+            case GPS_RELEASE_SUPL_DATA_CONN:
+                if (mSuplDataConnectionState != SUPL_DATA_CONNECTION_CLOSED) {
+                    mConnMgr.stopUsingNetworkFeature(
+                            ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL);
+                    native_supl_data_conn_closed();
+                    mSuplDataConnectionState = SUPL_DATA_CONNECTION_CLOSED;
+                }
+                break;
+            case GPS_SUPL_DATA_CONNECTED:
+                // Log.d(TAG, "GPS_SUPL_DATA_CONNECTED");
+                break;
+            case GPS_SUPL_DATA_CONN_DONE:
+                // Log.d(TAG, "GPS_SUPL_DATA_CONN_DONE");
+                break;
+            case GPS_SUPL_DATA_CONN_FAILED:
+                // Log.d(TAG, "GPS_SUPL_DATA_CONN_FAILED");
+                break;
+        }
+    }
+
     private void xtraDownloadRequest() {
         if (Config.LOGD) Log.d(TAG, "xtraDownloadRequest");
         if (mNetworkThread != null) {
@@ -1002,6 +1060,8 @@
     private native void native_inject_xtra_data(byte[] data, int length);
 
     // SUPL Support    
+    private native void native_supl_data_conn_open(String apn);
+    private native void native_supl_data_conn_closed();
+    private native void native_supl_data_conn_failed();
     private native void native_set_supl_server(int addr, int port);
-    private native void native_set_supl_apn(String apn);
 }