Merge change 20290
* changes: gps: Add GpsStatus.NmeaListener interface for receiving NMEA sentences.
This commit is contained in:
@@ -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.
|
||||
memcpy(&sGpsLocationCopy, &sGpsLocation, sizeof(sGpsLocationCopy));
|
||||
memcpy(&sGpsStatusCopy, &sGpsStatus, sizeof(sGpsStatusCopy));
|
||||
memcpy(&sGpsSvStatusCopy, &sGpsSvStatus, sizeof(sGpsSvStatusCopy));
|
||||
memcpy(&sAGpsStatusCopy, &sAGpsStatus, sizeof(sAGpsStatusCopy));
|
||||
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},
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,49 +1125,103 @@ 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() {
|
||||
Message msg = Message.obtain();
|
||||
msg.what = GpsStatus.GPS_EVENT_STARTED;
|
||||
mGpsHandler.sendMessage(msg);
|
||||
if (mListener != null) {
|
||||
Message msg = Message.obtain();
|
||||
msg.what = GpsStatus.GPS_EVENT_STARTED;
|
||||
mGpsHandler.sendMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
public void onGpsStopped() {
|
||||
Message msg = Message.obtain();
|
||||
msg.what = GpsStatus.GPS_EVENT_STOPPED;
|
||||
mGpsHandler.sendMessage(msg);
|
||||
if (mListener != null) {
|
||||
Message msg = Message.obtain();
|
||||
msg.what = GpsStatus.GPS_EVENT_STOPPED;
|
||||
mGpsHandler.sendMessage(msg);
|
||||
}
|
||||
}
|
||||
|
||||
public void onFirstFix(int ttff) {
|
||||
mGpsStatus.setTimeToFirstFix(ttff);
|
||||
Message msg = Message.obtain();
|
||||
msg.what = GpsStatus.GPS_EVENT_FIRST_FIX;
|
||||
mGpsHandler.sendMessage(msg);
|
||||
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) {
|
||||
mGpsStatus.setStatus(svCount, prns, snrs, elevations, azimuths,
|
||||
ephemerisMask, almanacMask, usedInFixMask);
|
||||
if (mListener != null) {
|
||||
mGpsStatus.setStatus(svCount, prns, snrs, elevations, azimuths,
|
||||
ephemerisMask, almanacMask, usedInFixMask);
|
||||
|
||||
Message msg = Message.obtain();
|
||||
msg.what = GpsStatus.GPS_EVENT_SATELLITE_STATUS;
|
||||
// remove any SV status messages already in the queue
|
||||
mGpsHandler.removeMessages(GpsStatus.GPS_EVENT_SATELLITE_STATUS);
|
||||
mGpsHandler.sendMessage(msg);
|
||||
Message msg = Message.obtain();
|
||||
msg.what = GpsStatus.GPS_EVENT_SATELLITE_STATUS;
|
||||
// remove any SV status messages already in the queue
|
||||
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) {
|
||||
// synchronize on mGpsStatus to ensure the data is copied atomically.
|
||||
synchronized(mGpsStatus) {
|
||||
mListener.onGpsStatusChanged(msg.what);
|
||||
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}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user