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

Commit 4147f49c authored by Android (Google) Code Review's avatar Android (Google) Code Review
Browse files

Merge change 20290

* changes:
  gps: Add GpsStatus.NmeaListener interface for receiving NMEA sentences.
parents f8e136dc b16e7800
Loading
Loading
Loading
Loading
+72 −4
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ static jmethodID method_reportLocation;
static jmethodID method_reportStatus;
static jmethodID method_reportSvStatus;
static jmethodID method_reportAGpsStatus;
static jmethodID method_reportNmea;
static jmethodID method_xtraDownloadRequest;

static const GpsInterface* sGpsInterface = NULL;
@@ -44,12 +45,23 @@ static GpsStatus sGpsStatus;
static GpsSvStatus  sGpsSvStatus;
static AGpsStatus   sAGpsStatus;

// buffer for NMEA data
#define NMEA_SENTENCE_LENGTH    100
#define NMEA_SENTENCE_COUNT     40
struct NmeaSentence {
    GpsUtcTime  timestamp;
    char        nmea[NMEA_SENTENCE_LENGTH];
};
static NmeaSentence sNmeaBuffer[NMEA_SENTENCE_LENGTH];
static int mNmeaSentenceCount = 0;

// 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 AGpsStatus   sAGpsStatusCopy;
static NmeaSentence sNmeaBufferCopy[NMEA_SENTENCE_LENGTH];

enum CallbackType {
    kLocation = 1,
@@ -58,6 +70,7 @@ enum CallbackType {
    kAGpsStatus = 8,
    kXtraDownloadRequest = 16,
    kDisableRequest = 32,
    kNmeaAvailable = 64,
}; 
static int sPendingCallbacks;

@@ -96,6 +109,30 @@ static void sv_status_callback(GpsSvStatus* sv_status)
    pthread_mutex_unlock(&sEventMutex);
}

static void nmea_callback(GpsUtcTime timestamp, const char* nmea, int length)
{
    pthread_mutex_lock(&sEventMutex);

    if (length >= NMEA_SENTENCE_LENGTH) {
        LOGE("NMEA data too long in nmea_callback (length = %d)\n", length);
        length = NMEA_SENTENCE_LENGTH - 1;
    }
    if (mNmeaSentenceCount >= NMEA_SENTENCE_COUNT) {
        LOGE("NMEA data overflowed buffer\n");
        pthread_mutex_unlock(&sEventMutex);
        return;
    }

    sPendingCallbacks |= kNmeaAvailable;
    sNmeaBuffer[mNmeaSentenceCount].timestamp = timestamp;
    memcpy(sNmeaBuffer[mNmeaSentenceCount].nmea, nmea, length);
    sNmeaBuffer[mNmeaSentenceCount].nmea[length] = 0;
    mNmeaSentenceCount++;

    pthread_cond_signal(&sEventCond);
    pthread_mutex_unlock(&sEventMutex);
}

static void agps_status_callback(AGpsStatus* agps_status)
{
    pthread_mutex_lock(&sEventMutex);
@@ -111,6 +148,7 @@ GpsCallbacks sGpsCallbacks = {
    location_callback,
    status_callback,
    sv_status_callback,
    nmea_callback
};

static void
@@ -135,6 +173,7 @@ static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env,
    method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V");
    method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "()V");
    method_reportAGpsStatus = env->GetMethodID(clazz, "reportAGpsStatus", "(II)V");
    method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(IJ)V");
    method_xtraDownloadRequest = env->GetMethodID(clazz, "xtraDownloadRequest", "()V");
}

@@ -200,13 +239,21 @@ static void android_location_GpsLocationProvider_wait_for_event(JNIEnv* env, job
    // copy and clear the callback flags
    int pendingCallbacks = sPendingCallbacks;
    sPendingCallbacks = 0;
    int nmeaSentenceCount = mNmeaSentenceCount;
    mNmeaSentenceCount = 0;
    
    // copy everything and unlock the mutex before calling into Java code to avoid the possibility
    // of timeouts in the GPS engine.
    if (pendingCallbacks & kLocation)
        memcpy(&sGpsLocationCopy, &sGpsLocation, sizeof(sGpsLocationCopy));
    if (pendingCallbacks & kStatus)
        memcpy(&sGpsStatusCopy, &sGpsStatus, sizeof(sGpsStatusCopy));
    if (pendingCallbacks & kSvStatus)
        memcpy(&sGpsSvStatusCopy, &sGpsSvStatus, sizeof(sGpsSvStatusCopy));
    if (pendingCallbacks & kAGpsStatus)
        memcpy(&sAGpsStatusCopy, &sAGpsStatus, sizeof(sAGpsStatusCopy));
    if (pendingCallbacks & kNmeaAvailable)
        memcpy(&sNmeaBufferCopy, &sNmeaBuffer, nmeaSentenceCount * sizeof(sNmeaBuffer[0]));
    pthread_mutex_unlock(&sEventMutex);   

    if (pendingCallbacks & kLocation) { 
@@ -225,6 +272,11 @@ static void android_location_GpsLocationProvider_wait_for_event(JNIEnv* env, job
    if (pendingCallbacks & kAGpsStatus) {
        env->CallVoidMethod(obj, method_reportAGpsStatus, sAGpsStatusCopy.type, sAGpsStatusCopy.status);
    }  
    if (pendingCallbacks & kNmeaAvailable) {
        for (int i = 0; i < nmeaSentenceCount; i++) {
            env->CallVoidMethod(obj, method_reportNmea, i, sNmeaBuffer[i].timestamp);
        }
    }
    if (pendingCallbacks & kXtraDownloadRequest) {    
        env->CallVoidMethod(obj, method_xtraDownloadRequest);
    }
@@ -264,6 +316,21 @@ static jint android_location_GpsLocationProvider_read_sv_status(JNIEnv* env, job
    return num_svs;
}

static jint android_location_GpsLocationProvider_read_nmea(JNIEnv* env, jobject obj, jint index, jbyteArray nmeaArray, jint buffer_size)
{
    // this should only be called from within a call to reportStatus, so we don't need to lock here

    jbyte* nmea = env->GetByteArrayElements(nmeaArray, 0);

    int length = strlen(sNmeaBuffer[index].nmea);
    if (length > buffer_size)
        length = buffer_size;
    memcpy(nmea, sNmeaBuffer[index].nmea, length);

    env->ReleaseByteArrayElements(nmeaArray, nmea, 0);
    return length;
}

static void android_location_GpsLocationProvider_inject_time(JNIEnv* env, jobject obj, jlong time, 
        jlong timeReference, jint uncertainty)
{
@@ -360,6 +427,7 @@ static JNINativeMethod sMethods[] = {
    {"native_delete_aiding_data", "(I)V", (void*)android_location_GpsLocationProvider_delete_aiding_data},
    {"native_wait_for_event", "()V", (void*)android_location_GpsLocationProvider_wait_for_event},
    {"native_read_sv_status", "([I[F[F[F[I)I", (void*)android_location_GpsLocationProvider_read_sv_status},
    {"native_read_nmea", "(I[BI)I", (void*)android_location_GpsLocationProvider_read_nmea},
    {"native_inject_time", "(JJI)V", (void*)android_location_GpsLocationProvider_inject_time},
    {"native_inject_location", "(DDF)V", (void*)android_location_GpsLocationProvider_inject_location},
    {"native_supports_xtra", "()Z", (void*)android_location_GpsLocationProvider_supports_xtra},
+9 −0
Original line number Diff line number Diff line
@@ -115,6 +115,15 @@ public final class GpsStatus {
        void onGpsStatusChanged(int event);
    }

    /**
     * Used for receiving NMEA data from the GPS.
     *
     * {@hide}
     */
    public interface NmeaListener {
        void onNmeaReceived(long timestamp, String nmea);
    }

    GpsStatus() {
        for (int i = 0; i < mSatellites.length; i++) {
            mSatellites[i] = new GpsSatellite(i + 1);
+1 −0
Original line number Diff line number Diff line
@@ -29,4 +29,5 @@ oneway interface IGpsStatusListener
    void onSvStatusChanged(int svCount, in int[] prns, in float[] snrs, 
            in float[] elevations, in float[] azimuths, 
            int ephemerisMask, int almanacMask, int usedInFixMask);
    void onNmeaReceived(long timestamp, String nmea);
}
+126 −20
Original line number Diff line number Diff line
@@ -51,6 +51,8 @@ public class LocationManager {
    private ILocationManager mService;
    private final HashMap<GpsStatus.Listener, GpsStatusListenerTransport> mGpsStatusListeners =
            new HashMap<GpsStatus.Listener, GpsStatusListenerTransport>();
    private final HashMap<GpsStatus.NmeaListener, GpsStatusListenerTransport> mNmeaListeners =
            new HashMap<GpsStatus.NmeaListener, GpsStatusListenerTransport>();
    private final GpsStatus mGpsStatus = new GpsStatus();

    /**
@@ -1123,33 +1125,62 @@ public class LocationManager {
    private class GpsStatusListenerTransport extends IGpsStatusListener.Stub {

        private final GpsStatus.Listener mListener;
        private final GpsStatus.NmeaListener mNmeaListener;

        // This must not equal any of the GpsStatus event IDs
        private static final int NMEA_RECEIVED = 1000;

        private class Nmea {
            long mTimestamp;
            String mNmea;

            Nmea(long timestamp, String nmea) {
                mTimestamp = timestamp;
                mNmea = nmea;
            }
        }
        private ArrayList<Nmea> mNmeaBuffer;

        GpsStatusListenerTransport(GpsStatus.Listener listener) {
            mListener = listener;
            mNmeaListener = null;
        }

        GpsStatusListenerTransport(GpsStatus.NmeaListener listener) {
            mNmeaListener = listener;
            mListener = null;
            mNmeaBuffer = new ArrayList<Nmea>();
        }

        public void onGpsStarted() {
            if (mListener != null) {
                Message msg = Message.obtain();
                msg.what = GpsStatus.GPS_EVENT_STARTED;
                mGpsHandler.sendMessage(msg);
            }
        }

        public void onGpsStopped() {
            if (mListener != null) {
                Message msg = Message.obtain();
                msg.what = GpsStatus.GPS_EVENT_STOPPED;
                mGpsHandler.sendMessage(msg);
            }
        }

        public void onFirstFix(int ttff) {
            if (mListener != null) {
                mGpsStatus.setTimeToFirstFix(ttff);
                Message msg = Message.obtain();
                msg.what = GpsStatus.GPS_EVENT_FIRST_FIX;
                mGpsHandler.sendMessage(msg);
            }
        }

        public void onSvStatusChanged(int svCount, int[] prns, float[] snrs,
                float[] elevations, float[] azimuths, int ephemerisMask,
                int almanacMask, int usedInFixMask) {
            if (mListener != null) {
                mGpsStatus.setStatus(svCount, prns, snrs, elevations, azimuths,
                        ephemerisMask, almanacMask, usedInFixMask);

@@ -1159,15 +1190,40 @@ public class LocationManager {
                mGpsHandler.removeMessages(GpsStatus.GPS_EVENT_SATELLITE_STATUS);
                mGpsHandler.sendMessage(msg);
            }
        }

        public void onNmeaReceived(long timestamp, String nmea) {
            if (mNmeaListener != null) {
                synchronized (mNmeaBuffer) {
                    mNmeaBuffer.add(new Nmea(timestamp, nmea));
                }
                Message msg = Message.obtain();
                msg.what = NMEA_RECEIVED;
                // remove any NMEA_RECEIVED messages already in the queue
                mGpsHandler.removeMessages(NMEA_RECEIVED);
                mGpsHandler.sendMessage(msg);
            }
        }

        private final Handler mGpsHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                if (msg.what == NMEA_RECEIVED) {
                    synchronized (mNmeaBuffer) {
                        int length = mNmeaBuffer.size();
                        for (int i = 0; i < length; i++) {
                            Nmea nmea = mNmeaBuffer.get(i);
                            mNmeaListener.onNmeaReceived(nmea.mTimestamp, nmea.mNmea);
                        }
                        mNmeaBuffer.clear();
                    }
                } else {
                    // synchronize on mGpsStatus to ensure the data is copied atomically.
                    synchronized(mGpsStatus) {
                        mListener.onGpsStatusChanged(msg.what);
                    }
                }
            }
        };
    }

@@ -1217,6 +1273,56 @@ public class LocationManager {
        }
    }

    /**
     * Adds an NMEA listener.
     *
     * @param listener NMEA listener object to register
     *
     * @return true if the listener was successfully added
     *
     * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
     *
     * {@hide}
     */
    public boolean addNmeaListener(GpsStatus.NmeaListener listener) {
        boolean result;

        if (mNmeaListeners.get(listener) != null) {
            // listener is already registered
            return true;
        }
        try {
            GpsStatusListenerTransport transport = new GpsStatusListenerTransport(listener);
            result = mService.addGpsStatusListener(transport);
            if (result) {
                mNmeaListeners.put(listener, transport);
            }
        } catch (RemoteException e) {
            Log.e(TAG, "RemoteException in registerGpsStatusListener: ", e);
            result = false;
        }

        return result;
    }

    /**
     * Removes an NMEA listener.
     *
     * @param listener NMEA listener object to remove
     *
     * {@hide}
     */
    public void removeNmeaListener(GpsStatus.NmeaListener listener) {
        try {
            GpsStatusListenerTransport transport = mNmeaListeners.remove(listener);
            if (transport != null) {
                mService.removeGpsStatusListener(transport);
            }
        } catch (RemoteException e) {
            Log.e(TAG, "RemoteException in unregisterGpsStatusListener: ", e);
        }
    }

     /**
     * Retrieves information about the current status of the GPS engine.
     * This should only be called from the {@link GpsStatus.Listener#onGpsStatusChanged}
+29 −0
Original line number Diff line number Diff line
@@ -1014,6 +1014,32 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
        }
    }

    /**
     * called from native code to report NMEA data received
     */
    private void reportNmea(int index, long timestamp) {
        synchronized(mListeners) {
            int size = mListeners.size();
            if (size > 0) {
                // don't bother creating the String if we have no listeners
                int length = native_read_nmea(index, mNmeaBuffer, mNmeaBuffer.length);
                String nmea = new String(mNmeaBuffer, 0, length);

                for (int i = 0; i < size; i++) {
                    Listener listener = mListeners.get(i);
                    try {
                        listener.mListener.onNmeaReceived(timestamp, nmea);
                    } catch (RemoteException e) {
                        Log.w(TAG, "RemoteException in reportNmea");
                        mListeners.remove(listener);
                        // adjust for size of list changing
                        size--;
                    }
                }
            }
        }
    }

    private void xtraDownloadRequest() {
        if (Config.LOGD) Log.d(TAG, "xtraDownloadRequest");
        if (mNetworkThread != null) {
@@ -1194,6 +1220,8 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
    private float mSvAzimuths[] = new float[MAX_SVS];
    private int mSvMasks[] = new int[3];
    private int mSvCount;
    // preallocated to avoid memory allocation in reportNmea()
    private byte[] mNmeaBuffer = new byte[120];

    static { class_init_native(); }
    private static native void class_init_native();
@@ -1211,6 +1239,7 @@ public class GpsLocationProvider extends ILocationProvider.Stub {
    // mask[0] is ephemeris mask and mask[1] is almanac mask
    private native int native_read_sv_status(int[] svs, float[] snrs,
            float[] elevations, float[] azimuths, int[] masks);
    private native int native_read_nmea(int index, byte[] buffer, int bufferSize);
    private native void native_inject_location(double latitude, double longitude, float accuracy);

    // XTRA Support