Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 54f6e80d authored by destradaa's avatar destradaa Committed by Android (Google) Code Review
Browse files

Merge "IPv6 support for GPS HAL."

parents cc8f7ae6 96a14701
Loading
Loading
Loading
Loading
+121 −35
Original line number Diff line number Diff line
@@ -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) {
    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 {
                if (cursor.moveToFirst()) {
                    apn = cursor.getString(0);
            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);
+105 −13
Original line number Diff line number Diff line
@@ -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);
    }

    if (isSupported) {
        env->CallVoidMethod(mCallbacksObj, method_reportAGpsStatus, agps_status->type,
                            agps_status->status, byteArray);

    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__);
    } 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);

    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},