summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--services/core/java/com/android/server/location/GpsLocationProvider.java156
-rw-r--r--services/core/jni/com_android_server_location_GpsLocationProvider.cpp118
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},