diff options
| -rw-r--r-- | services/core/java/com/android/server/location/GpsLocationProvider.java | 156 | ||||
| -rw-r--r-- | services/core/jni/com_android_server_location_GpsLocationProvider.cpp | 118 |
2 files changed, 226 insertions, 48 deletions
diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java index c6cf68f..c5b6c7b 100644 --- a/services/core/java/com/android/server/location/GpsLocationProvider.java +++ b/services/core/java/com/android/server/location/GpsLocationProvider.java @@ -83,6 +83,8 @@ import java.util.ArrayList; import java.util.Date; import java.util.Map.Entry; import java.util.Properties; +import java.net.InetAddress; +import java.net.UnknownHostException; /** * A GPS implementation of LocationProvider used by LocationManager. @@ -158,6 +160,12 @@ public class GpsLocationProvider implements LocationProviderInterface { private static final int AGPS_TYPE_SUPL = 1; private static final int AGPS_TYPE_C2K = 2; + // these must match the definitions in gps.h + private static final int APN_INVALID = 0; + private static final int APN_IPV4 = 1; + private static final int APN_IPV6 = 2; + private static final int APN_IPV4V6 = 3; + // for mAGpsDataConnectionState private static final int AGPS_DATA_CONNECTION_CLOSED = 0; private static final int AGPS_DATA_CONNECTION_OPENING = 1; @@ -312,8 +320,9 @@ public class GpsLocationProvider implements LocationProviderInterface { private Handler mHandler; private String mAGpsApn; + private int mApnIpType; private int mAGpsDataConnectionState; - private int mAGpsDataConnectionIpAddr; + private InetAddress mAGpsDataConnectionIpAddr; private final ConnectivityManager mConnMgr; private final GpsNetInitiatedHandler mNIHandler; @@ -595,28 +604,28 @@ public class GpsLocationProvider implements LocationProviderInterface { if (info != null && info.getType() == ConnectivityManager.TYPE_MOBILE_SUPL && mAGpsDataConnectionState == AGPS_DATA_CONNECTION_OPENING) { - String apnName = info.getExtraInfo(); if (mNetworkAvailable) { + String apnName = info.getExtraInfo(); if (apnName == null) { /* Assign a dummy value in the case of C2K as otherwise we will have a runtime exception in the following call to native_agps_data_conn_open*/ apnName = "dummy-apn"; } mAGpsApn = apnName; - if (DEBUG) Log.d(TAG, "mAGpsDataConnectionIpAddr " + mAGpsDataConnectionIpAddr); - if (mAGpsDataConnectionIpAddr != 0xffffffff) { - boolean route_result; - if (DEBUG) Log.d(TAG, "call requestRouteToHost"); - route_result = mConnMgr.requestRouteToHost(ConnectivityManager.TYPE_MOBILE_SUPL, - mAGpsDataConnectionIpAddr); - if (route_result == false) Log.d(TAG, "call requestRouteToHost failed"); + mApnIpType = getApnIpType(apnName); + setRouting(); + if (DEBUG) { + String message = String.format( + "native_agps_data_conn_open: mAgpsApn=%s, mApnIpType=%s", + mAGpsApn, mApnIpType); + Log.d(TAG, message); } - if (DEBUG) Log.d(TAG, "call native_agps_data_conn_open"); - native_agps_data_conn_open(apnName); + native_agps_data_conn_open(mAGpsApn, mApnIpType); mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN; } else { - if (DEBUG) Log.d(TAG, "call native_agps_data_conn_failed"); + Log.e(TAG, "call native_agps_data_conn_failed, info: " + info); mAGpsApn = null; + mApnIpType = APN_INVALID; mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED; native_agps_data_conn_failed(); } @@ -1324,7 +1333,7 @@ public class GpsLocationProvider implements LocationProviderInterface { /** * called from native code to update AGPS status */ - private void reportAGpsStatus(int type, int status, int ipaddr) { + private void reportAGpsStatus(int type, int status, byte[] ipaddr) { switch (status) { case GPS_REQUEST_AGPS_DATA_CONN: if (DEBUG) Log.d(TAG, "GPS_REQUEST_AGPS_DATA_CONN"); @@ -1333,20 +1342,20 @@ public class GpsLocationProvider implements LocationProviderInterface { mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPENING; int result = mConnMgr.startUsingNetworkFeature( ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL); - mAGpsDataConnectionIpAddr = ipaddr; + if (ipaddr != null) { + try { + mAGpsDataConnectionIpAddr = InetAddress.getByAddress(ipaddr); + } catch (UnknownHostException e) { + Log.e(TAG, "Bad IP Address: " + ipaddr, e); + mAGpsDataConnectionIpAddr = null; + } + } + if (result == PhoneConstants.APN_ALREADY_ACTIVE) { if (DEBUG) Log.d(TAG, "PhoneConstants.APN_ALREADY_ACTIVE"); if (mAGpsApn != null) { - Log.d(TAG, "mAGpsDataConnectionIpAddr " + mAGpsDataConnectionIpAddr); - if (mAGpsDataConnectionIpAddr != 0xffffffff) { - boolean route_result; - if (DEBUG) Log.d(TAG, "call requestRouteToHost"); - route_result = mConnMgr.requestRouteToHost( - ConnectivityManager.TYPE_MOBILE_SUPL, - mAGpsDataConnectionIpAddr); - if (route_result == false) Log.d(TAG, "call requestRouteToHost failed"); - } - native_agps_data_conn_open(mAGpsApn); + setRouting(); + native_agps_data_conn_open(mAGpsApn, mApnIpType); mAGpsDataConnectionState = AGPS_DATA_CONNECTION_OPEN; } else { Log.e(TAG, "mAGpsApn not set when receiving PhoneConstants.APN_ALREADY_ACTIVE"); @@ -1370,6 +1379,7 @@ public class GpsLocationProvider implements LocationProviderInterface { ConnectivityManager.TYPE_MOBILE, Phone.FEATURE_ENABLE_SUPL); native_agps_data_conn_closed(); mAGpsDataConnectionState = AGPS_DATA_CONNECTION_CLOSED; + mAGpsDataConnectionIpAddr = null; } break; case GPS_AGPS_DATA_CONNECTED: @@ -1821,21 +1831,97 @@ public class GpsLocationProvider implements LocationProviderInterface { private String getSelectedApn() { Uri uri = Uri.parse("content://telephony/carriers/preferapn"); - String apn = null; + Cursor cursor = null; + try { + cursor = mContext.getContentResolver().query( + uri, + new String[] { "apn" }, + null /* selection */, + null /* selectionArgs */, + Carriers.DEFAULT_SORT_ORDER); + if (cursor != null && cursor.moveToFirst()) { + return cursor.getString(0); + } else { + Log.e(TAG, "No APN found to select."); + } + } catch (Exception e) { + Log.e(TAG, "Error encountered on selectiong the APN.", e); + } finally { + if (cursor != null) { + cursor.close(); + } + } - Cursor cursor = mContext.getContentResolver().query(uri, new String[] {"apn"}, - null, null, Carriers.DEFAULT_SORT_ORDER); + return null; + } - if (null != cursor) { - try { - if (cursor.moveToFirst()) { - apn = cursor.getString(0); - } - } finally { + private int getApnIpType(String apn) { + if (apn == null) { + return APN_INVALID; + } + + // look for cached data to use + if (apn.equals(mAGpsApn) && mApnIpType != APN_INVALID) { + return mApnIpType; + } + + String selection = String.format("current = 1 and apn = '%s' and carrier_enabled = 1", apn); + Cursor cursor = null; + try { + cursor = mContext.getContentResolver().query( + Carriers.CONTENT_URI, + new String[] { Carriers.PROTOCOL }, + selection, + null, + Carriers.DEFAULT_SORT_ORDER); + + if (null != cursor && cursor.moveToFirst()) { + return translateToApnIpType(cursor.getString(0), apn); + } else { + Log.e(TAG, "No entry found in query for APN: " + apn); + } + } catch (Exception e) { + Log.e(TAG, "Error encountered on APN query for: " + apn, e); + } finally { + if (cursor != null) { cursor.close(); } } - return apn; + + return APN_INVALID; + } + + private int translateToApnIpType(String ipProtocol, String apn) { + if ("IP".equals(ipProtocol)) { + return APN_IPV4; + } + if ("IPV6".equals(ipProtocol)) { + return APN_IPV6; + } + if ("IPV4V6".equals(ipProtocol)) { + return APN_IPV4V6; + } + + // we hit the default case so the ipProtocol is not recognized + String message = String.format("Unknown IP Protocol: %s, for APN: %s", ipProtocol, apn); + Log.e(TAG, message); + return APN_INVALID; + } + + private void setRouting() { + if (mAGpsDataConnectionIpAddr == null) { + return; + } + + boolean result = mConnMgr.requestRouteToHostAddress( + ConnectivityManager.TYPE_MOBILE_SUPL, + mAGpsDataConnectionIpAddr); + + if (!result) { + Log.e(TAG, "Error requesting route to host: " + mAGpsDataConnectionIpAddr); + } else if (DEBUG) { + Log.d(TAG, "Successfully requested route to host: " + mAGpsDataConnectionIpAddr); + } } @Override @@ -1897,7 +1983,7 @@ public class GpsLocationProvider implements LocationProviderInterface { private native String native_get_internal_state(); // AGPS Support - private native void native_agps_data_conn_open(String apn); + private native void native_agps_data_conn_open(String apn, int apnIpType); private native void native_agps_data_conn_closed(); private native void native_agps_data_conn_failed(); private native void native_agps_ni_message(byte [] msg, int length); diff --git a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp b/services/core/jni/com_android_server_location_GpsLocationProvider.cpp index e9ba116..5bafb52 100644 --- a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GpsLocationProvider.cpp @@ -30,6 +30,8 @@ #include <string.h> #include <pthread.h> +#include <linux/in.h> +#include <linux/in6.h> static jobject mCallbacksObj = NULL; @@ -168,19 +170,98 @@ GpsXtraCallbacks sGpsXtraCallbacks = { create_thread_callback, }; +static jbyteArray convert_to_ipv4(uint32_t ip, bool net_order) +{ + if (INADDR_NONE == ip) { + return NULL; + } + + JNIEnv* env = AndroidRuntime::getJNIEnv(); + jbyteArray byteArray = env->NewByteArray(4); + if (byteArray == NULL) { + ALOGE("Unable to allocate byte array for IPv4 address"); + return NULL; + } + + jbyte ipv4[4]; + if (net_order) { + memcpy(ipv4, &ip, sizeof(ipv4)); + } else { + //endianess transparent conversion from int to char[] + ipv4[0] = (jbyte) (ip & 0xFF); + ipv4[1] = (jbyte)((ip>>8) & 0xFF); + ipv4[2] = (jbyte)((ip>>16) & 0xFF); + ipv4[3] = (jbyte) (ip>>24); + } + + env->SetByteArrayRegion(byteArray, 0, 4, (const jbyte*) ipv4); + return byteArray; +} + static void agps_status_callback(AGpsStatus* agps_status) { JNIEnv* env = AndroidRuntime::getJNIEnv(); + jbyteArray byteArray = NULL; + bool isSupported = false; + + size_t status_size = agps_status->size; + if (status_size == sizeof(AGpsStatus_v3)) { + switch (agps_status->addr.ss_family) + { + case AF_INET: + { + struct sockaddr_in *in = (struct sockaddr_in*)&(agps_status->addr); + uint32_t *pAddr = (uint32_t*)&(in->sin_addr); + byteArray = convert_to_ipv4(*pAddr, true /* net_order */); + if (byteArray != NULL) { + isSupported = true; + } + } + break; + case AF_INET6: + { + struct sockaddr_in6 *in6 = (struct sockaddr_in6*)&(agps_status->addr); + byteArray = env->NewByteArray(16); + if (byteArray != NULL) { + env->SetByteArrayRegion(byteArray, 0, 16, (const jbyte *)&(in6->sin6_addr)); + isSupported = true; + } else { + ALOGE("Unable to allocate byte array for IPv6 address."); + } + } + break; + default: + ALOGE("Invalid ss_family found: %d", agps_status->addr.ss_family); + break; + } + } else if (status_size >= sizeof(AGpsStatus_v2)) { + // for back-compatibility reasons we check in v2 that the data structure size is greater or + // equal to the declared size in gps.h + uint32_t ipaddr = agps_status->ipaddr; + byteArray = convert_to_ipv4(ipaddr, false /* net_order */); + if (ipaddr == INADDR_NONE || byteArray != NULL) { + isSupported = true; + } + } else if (status_size >= sizeof(AGpsStatus_v1)) { + // because we have to check for >= with regards to v2, we also need to relax the check here + // and only make sure that the size is at least what we expect + isSupported = true; + } else { + ALOGE("Invalid size of AGpsStatus found: %d.", status_size); + } - uint32_t ipaddr; - // ipaddr field was not included in original AGpsStatus - if (agps_status->size >= sizeof(AGpsStatus)) - ipaddr = agps_status->ipaddr; - else - ipaddr = 0xFFFFFFFF; - env->CallVoidMethod(mCallbacksObj, method_reportAGpsStatus, - agps_status->type, agps_status->status, ipaddr); - checkAndClearExceptionFromCallback(env, __FUNCTION__); + if (isSupported) { + env->CallVoidMethod(mCallbacksObj, method_reportAGpsStatus, agps_status->type, + agps_status->status, byteArray); + + checkAndClearExceptionFromCallback(env, __FUNCTION__); + } else { + ALOGD("Skipping calling method_reportAGpsStatus."); + } + + if (byteArray) { + env->DeleteLocalRef(byteArray); + } } AGpsCallbacks sAGpsCallbacks = { @@ -339,7 +420,7 @@ static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, method_reportLocation = env->GetMethodID(clazz, "reportLocation", "(IDDDFFFJ)V"); method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V"); method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "()V"); - method_reportAGpsStatus = env->GetMethodID(clazz, "reportAGpsStatus", "(III)V"); + method_reportAGpsStatus = env->GetMethodID(clazz, "reportAGpsStatus", "(II[B)V"); method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(J)V"); method_setEngineCapabilities = env->GetMethodID(clazz, "setEngineCapabilities", "(I)V"); method_xtraDownloadRequest = env->GetMethodID(clazz, "xtraDownloadRequest", "()V"); @@ -610,7 +691,8 @@ static void android_location_GpsLocationProvider_inject_xtra_data(JNIEnv* env, j env->ReleasePrimitiveArrayCritical(data, bytes, JNI_ABORT); } -static void android_location_GpsLocationProvider_agps_data_conn_open(JNIEnv* env, jobject obj, jstring apn) +static void android_location_GpsLocationProvider_agps_data_conn_open( + JNIEnv* env, jobject obj, jstring apn, jint apnIpType) { if (!sAGpsInterface) { ALOGE("no AGPS interface in agps_data_conn_open"); @@ -620,8 +702,18 @@ static void android_location_GpsLocationProvider_agps_data_conn_open(JNIEnv* env jniThrowException(env, "java/lang/IllegalArgumentException", NULL); return; } + const char *apnStr = env->GetStringUTFChars(apn, NULL); - sAGpsInterface->data_conn_open(apnStr); + + size_t interface_size = sAGpsInterface->size; + if (interface_size == sizeof(AGpsInterface_v2)) { + sAGpsInterface->data_conn_open_with_apn_ip_type(apnStr, apnIpType); + } else if (interface_size == sizeof(AGpsInterface_v1)) { + sAGpsInterface->data_conn_open(apnStr); + } else { + ALOGE("Invalid size of AGpsInterface found: %d.", interface_size); + } + env->ReleaseStringUTFChars(apn, apnStr); } @@ -775,7 +867,7 @@ static JNINativeMethod sMethods[] = { {"native_inject_location", "(DDF)V", (void*)android_location_GpsLocationProvider_inject_location}, {"native_supports_xtra", "()Z", (void*)android_location_GpsLocationProvider_supports_xtra}, {"native_inject_xtra_data", "([BI)V", (void*)android_location_GpsLocationProvider_inject_xtra_data}, - {"native_agps_data_conn_open", "(Ljava/lang/String;)V", (void*)android_location_GpsLocationProvider_agps_data_conn_open}, + {"native_agps_data_conn_open", "(Ljava/lang/String;I)V", (void*)android_location_GpsLocationProvider_agps_data_conn_open}, {"native_agps_data_conn_closed", "()V", (void*)android_location_GpsLocationProvider_agps_data_conn_closed}, {"native_agps_data_conn_failed", "()V", (void*)android_location_GpsLocationProvider_agps_data_conn_failed}, {"native_agps_set_id","(ILjava/lang/String;)V",(void*)android_location_GpsLocationProvider_agps_set_id}, |
