1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#define LOG_TAG "GpsLocationProvider" 18 19//#define LOG_NDDEBUG 0 20 21#include "JNIHelp.h" 22#include "jni.h" 23#include "hardware_legacy/gps.h" 24#include "hardware_legacy/gps_ni.h" 25#include "utils/Log.h" 26#include "utils/misc.h" 27 28#include <string.h> 29#include <pthread.h> 30 31static pthread_mutex_t sEventMutex = PTHREAD_MUTEX_INITIALIZER; 32static pthread_cond_t sEventCond = PTHREAD_COND_INITIALIZER; 33static jmethodID method_reportLocation; 34static jmethodID method_reportStatus; 35static jmethodID method_reportSvStatus; 36static jmethodID method_reportAGpsStatus; 37static jmethodID method_reportNmea; 38static jmethodID method_xtraDownloadRequest; 39static jmethodID method_reportNiNotification; 40 41static const GpsInterface* sGpsInterface = NULL; 42static const GpsXtraInterface* sGpsXtraInterface = NULL; 43static const AGpsInterface* sAGpsInterface = NULL; 44static const GpsPrivacyInterface* sGpsPrivacyInterface = NULL; 45static const GpsNiInterface* sGpsNiInterface = NULL; 46static const GpsDebugInterface* sGpsDebugInterface = NULL; 47 48// data written to by GPS callbacks 49static GpsLocation sGpsLocation; 50static GpsStatus sGpsStatus; 51static GpsSvStatus sGpsSvStatus; 52static AGpsStatus sAGpsStatus; 53static GpsNiNotification sGpsNiNotification; 54 55// buffer for NMEA data 56#define NMEA_SENTENCE_LENGTH 100 57#define NMEA_SENTENCE_COUNT 40 58struct NmeaSentence { 59 GpsUtcTime timestamp; 60 char nmea[NMEA_SENTENCE_LENGTH]; 61}; 62static NmeaSentence sNmeaBuffer[NMEA_SENTENCE_COUNT]; 63static int mNmeaSentenceCount = 0; 64 65// a copy of the data shared by android_location_GpsLocationProvider_wait_for_event 66// and android_location_GpsLocationProvider_read_status 67static GpsLocation sGpsLocationCopy; 68static GpsStatus sGpsStatusCopy; 69static GpsSvStatus sGpsSvStatusCopy; 70static AGpsStatus sAGpsStatusCopy; 71static NmeaSentence sNmeaBufferCopy[NMEA_SENTENCE_COUNT]; 72static GpsNiNotification sGpsNiNotificationCopy; 73 74enum CallbackType { 75 kLocation = 1, 76 kStatus = 2, 77 kSvStatus = 4, 78 kAGpsStatus = 8, 79 kXtraDownloadRequest = 16, 80 kDisableRequest = 32, 81 kNmeaAvailable = 64, 82 kNiNotification = 128, 83}; 84static int sPendingCallbacks; 85 86namespace android { 87 88static void location_callback(GpsLocation* location) 89{ 90 pthread_mutex_lock(&sEventMutex); 91 92 sPendingCallbacks |= kLocation; 93 memcpy(&sGpsLocation, location, sizeof(sGpsLocation)); 94 95 pthread_cond_signal(&sEventCond); 96 pthread_mutex_unlock(&sEventMutex); 97} 98 99static void status_callback(GpsStatus* status) 100{ 101 pthread_mutex_lock(&sEventMutex); 102 103 sPendingCallbacks |= kStatus; 104 memcpy(&sGpsStatus, status, sizeof(sGpsStatus)); 105 106 pthread_cond_signal(&sEventCond); 107 pthread_mutex_unlock(&sEventMutex); 108} 109 110static void sv_status_callback(GpsSvStatus* sv_status) 111{ 112 pthread_mutex_lock(&sEventMutex); 113 114 sPendingCallbacks |= kSvStatus; 115 memcpy(&sGpsSvStatus, sv_status, sizeof(GpsSvStatus)); 116 117 pthread_cond_signal(&sEventCond); 118 pthread_mutex_unlock(&sEventMutex); 119} 120 121static void nmea_callback(GpsUtcTime timestamp, const char* nmea, int length) 122{ 123 pthread_mutex_lock(&sEventMutex); 124 125 if (length >= NMEA_SENTENCE_LENGTH) { 126 LOGE("NMEA data too long in nmea_callback (length = %d)\n", length); 127 length = NMEA_SENTENCE_LENGTH - 1; 128 } 129 if (mNmeaSentenceCount >= NMEA_SENTENCE_COUNT) { 130 LOGE("NMEA data overflowed buffer\n"); 131 pthread_mutex_unlock(&sEventMutex); 132 return; 133 } 134 135 sPendingCallbacks |= kNmeaAvailable; 136 sNmeaBuffer[mNmeaSentenceCount].timestamp = timestamp; 137 memcpy(sNmeaBuffer[mNmeaSentenceCount].nmea, nmea, length); 138 sNmeaBuffer[mNmeaSentenceCount].nmea[length] = 0; 139 mNmeaSentenceCount++; 140 141 pthread_cond_signal(&sEventCond); 142 pthread_mutex_unlock(&sEventMutex); 143} 144 145static void agps_status_callback(AGpsStatus* agps_status) 146{ 147 pthread_mutex_lock(&sEventMutex); 148 149 sPendingCallbacks |= kAGpsStatus; 150 memcpy(&sAGpsStatus, agps_status, sizeof(AGpsStatus)); 151 152 pthread_cond_signal(&sEventCond); 153 pthread_mutex_unlock(&sEventMutex); 154} 155 156GpsCallbacks sGpsCallbacks = { 157 location_callback, 158 status_callback, 159 sv_status_callback, 160 nmea_callback 161}; 162 163static void 164download_request_callback() 165{ 166 pthread_mutex_lock(&sEventMutex); 167 sPendingCallbacks |= kXtraDownloadRequest; 168 pthread_cond_signal(&sEventCond); 169 pthread_mutex_unlock(&sEventMutex); 170} 171 172static void 173gps_ni_notify_callback(GpsNiNotification *notification) 174{ 175 LOGD("gps_ni_notify_callback: notif=%d", notification->notification_id); 176 177 pthread_mutex_lock(&sEventMutex); 178 179 sPendingCallbacks |= kNiNotification; 180 memcpy(&sGpsNiNotification, notification, sizeof(GpsNiNotification)); 181 182 pthread_cond_signal(&sEventCond); 183 pthread_mutex_unlock(&sEventMutex); 184} 185 186GpsXtraCallbacks sGpsXtraCallbacks = { 187 download_request_callback, 188}; 189 190AGpsCallbacks sAGpsCallbacks = { 191 agps_status_callback, 192}; 193 194GpsNiCallbacks sGpsNiCallbacks = { 195 gps_ni_notify_callback, 196}; 197 198static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, jclass clazz) { 199 method_reportLocation = env->GetMethodID(clazz, "reportLocation", "(IDDDFFFJ)V"); 200 method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V"); 201 method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "()V"); 202 method_reportAGpsStatus = env->GetMethodID(clazz, "reportAGpsStatus", "(II)V"); 203 method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(IJ)V"); 204 method_xtraDownloadRequest = env->GetMethodID(clazz, "xtraDownloadRequest", "()V"); 205 method_reportNiNotification = env->GetMethodID(clazz, "reportNiNotification", "(IIIIILjava/lang/String;Ljava/lang/String;IILjava/lang/String;)V"); 206} 207 208static jboolean android_location_GpsLocationProvider_is_supported(JNIEnv* env, jclass clazz) { 209 if (!sGpsInterface) 210 sGpsInterface = gps_get_interface(); 211 return (sGpsInterface != NULL); 212} 213 214static jboolean android_location_GpsLocationProvider_init(JNIEnv* env, jobject obj) 215{ 216 if (!sGpsInterface) 217 sGpsInterface = gps_get_interface(); 218 if (!sGpsInterface || sGpsInterface->init(&sGpsCallbacks) != 0) 219 return false; 220 221 if (!sAGpsInterface) 222 sAGpsInterface = (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE); 223 if (sAGpsInterface) 224 sAGpsInterface->init(&sAGpsCallbacks); 225 226 if (!sGpsNiInterface) 227 sGpsNiInterface = (const GpsNiInterface*)sGpsInterface->get_extension(GPS_NI_INTERFACE); 228 if (sGpsNiInterface) 229 sGpsNiInterface->init(&sGpsNiCallbacks); 230 231 // Clear privacy lock while enabled 232 if (!sGpsPrivacyInterface) 233 sGpsPrivacyInterface = (const GpsPrivacyInterface*)sGpsInterface->get_extension(GPS_PRIVACY_INTERFACE); 234 if (sGpsPrivacyInterface) 235 sGpsPrivacyInterface->set_privacy_lock(0); 236 237 if (!sGpsDebugInterface) 238 sGpsDebugInterface = (const GpsDebugInterface*)sGpsInterface->get_extension(GPS_DEBUG_INTERFACE); 239 240 return true; 241} 242 243static void android_location_GpsLocationProvider_disable(JNIEnv* env, jobject obj) 244{ 245 // Enable privacy lock while disabled 246 if (!sGpsPrivacyInterface) 247 sGpsPrivacyInterface = (const GpsPrivacyInterface*)sGpsInterface->get_extension(GPS_PRIVACY_INTERFACE); 248 if (sGpsPrivacyInterface) 249 sGpsPrivacyInterface->set_privacy_lock(1); 250 251 pthread_mutex_lock(&sEventMutex); 252 sPendingCallbacks |= kDisableRequest; 253 pthread_cond_signal(&sEventCond); 254 pthread_mutex_unlock(&sEventMutex); 255} 256 257static void android_location_GpsLocationProvider_cleanup(JNIEnv* env, jobject obj) 258{ 259 sGpsInterface->cleanup(); 260} 261 262static jboolean android_location_GpsLocationProvider_start(JNIEnv* env, jobject obj, jint positionMode, 263 jboolean singleFix, jint fixFrequency) 264{ 265 int result = sGpsInterface->set_position_mode(positionMode, (singleFix ? 0 : fixFrequency)); 266 if (result) { 267 return false; 268 } 269 270 return (sGpsInterface->start() == 0); 271} 272 273static jboolean android_location_GpsLocationProvider_stop(JNIEnv* env, jobject obj) 274{ 275 return (sGpsInterface->stop() == 0); 276} 277 278static void android_location_GpsLocationProvider_delete_aiding_data(JNIEnv* env, jobject obj, jint flags) 279{ 280 sGpsInterface->delete_aiding_data(flags); 281} 282 283static void android_location_GpsLocationProvider_wait_for_event(JNIEnv* env, jobject obj) 284{ 285 pthread_mutex_lock(&sEventMutex); 286 while (sPendingCallbacks == 0) { 287 pthread_cond_wait(&sEventCond, &sEventMutex); 288 } 289 290 // copy and clear the callback flags 291 int pendingCallbacks = sPendingCallbacks; 292 sPendingCallbacks = 0; 293 int nmeaSentenceCount = mNmeaSentenceCount; 294 mNmeaSentenceCount = 0; 295 296 // copy everything and unlock the mutex before calling into Java code to avoid the possibility 297 // of timeouts in the GPS engine. 298 if (pendingCallbacks & kLocation) 299 memcpy(&sGpsLocationCopy, &sGpsLocation, sizeof(sGpsLocationCopy)); 300 if (pendingCallbacks & kStatus) 301 memcpy(&sGpsStatusCopy, &sGpsStatus, sizeof(sGpsStatusCopy)); 302 if (pendingCallbacks & kSvStatus) 303 memcpy(&sGpsSvStatusCopy, &sGpsSvStatus, sizeof(sGpsSvStatusCopy)); 304 if (pendingCallbacks & kAGpsStatus) 305 memcpy(&sAGpsStatusCopy, &sAGpsStatus, sizeof(sAGpsStatusCopy)); 306 if (pendingCallbacks & kNmeaAvailable) 307 memcpy(&sNmeaBufferCopy, &sNmeaBuffer, nmeaSentenceCount * sizeof(sNmeaBuffer[0])); 308 if (pendingCallbacks & kNiNotification) 309 memcpy(&sGpsNiNotificationCopy, &sGpsNiNotification, sizeof(sGpsNiNotificationCopy)); 310 pthread_mutex_unlock(&sEventMutex); 311 312 if (pendingCallbacks & kLocation) { 313 env->CallVoidMethod(obj, method_reportLocation, sGpsLocationCopy.flags, 314 (jdouble)sGpsLocationCopy.latitude, (jdouble)sGpsLocationCopy.longitude, 315 (jdouble)sGpsLocationCopy.altitude, 316 (jfloat)sGpsLocationCopy.speed, (jfloat)sGpsLocationCopy.bearing, 317 (jfloat)sGpsLocationCopy.accuracy, (jlong)sGpsLocationCopy.timestamp); 318 } 319 if (pendingCallbacks & kStatus) { 320 env->CallVoidMethod(obj, method_reportStatus, sGpsStatusCopy.status); 321 } 322 if (pendingCallbacks & kSvStatus) { 323 env->CallVoidMethod(obj, method_reportSvStatus); 324 } 325 if (pendingCallbacks & kAGpsStatus) { 326 env->CallVoidMethod(obj, method_reportAGpsStatus, sAGpsStatusCopy.type, sAGpsStatusCopy.status); 327 } 328 if (pendingCallbacks & kNmeaAvailable) { 329 for (int i = 0; i < nmeaSentenceCount; i++) { 330 env->CallVoidMethod(obj, method_reportNmea, i, sNmeaBuffer[i].timestamp); 331 } 332 } 333 if (pendingCallbacks & kXtraDownloadRequest) { 334 env->CallVoidMethod(obj, method_xtraDownloadRequest); 335 } 336 if (pendingCallbacks & kDisableRequest) { 337 // don't need to do anything - we are just poking so wait_for_event will return. 338 } 339 if (pendingCallbacks & kNiNotification) { 340 LOGD("android_location_GpsLocationProvider_wait_for_event: sent notification callback."); 341 jstring reqId = env->NewStringUTF(sGpsNiNotificationCopy.requestor_id); 342 jstring text = env->NewStringUTF(sGpsNiNotificationCopy.text); 343 jstring extras = env->NewStringUTF(sGpsNiNotificationCopy.extras); 344 env->CallVoidMethod(obj, method_reportNiNotification, 345 sGpsNiNotificationCopy.notification_id, 346 sGpsNiNotificationCopy.ni_type, 347 sGpsNiNotificationCopy.notify_flags, 348 sGpsNiNotificationCopy.timeout, 349 sGpsNiNotificationCopy.default_response, 350 reqId, 351 text, 352 sGpsNiNotificationCopy.requestor_id_encoding, 353 sGpsNiNotificationCopy.text_encoding, 354 extras 355 ); 356 } 357} 358 359static jint android_location_GpsLocationProvider_read_sv_status(JNIEnv* env, jobject obj, 360 jintArray prnArray, jfloatArray snrArray, jfloatArray elevArray, jfloatArray azumArray, 361 jintArray maskArray) 362{ 363 // this should only be called from within a call to reportStatus, so we don't need to lock here 364 365 jint* prns = env->GetIntArrayElements(prnArray, 0); 366 jfloat* snrs = env->GetFloatArrayElements(snrArray, 0); 367 jfloat* elev = env->GetFloatArrayElements(elevArray, 0); 368 jfloat* azim = env->GetFloatArrayElements(azumArray, 0); 369 jint* mask = env->GetIntArrayElements(maskArray, 0); 370 371 int num_svs = sGpsSvStatusCopy.num_svs; 372 for (int i = 0; i < num_svs; i++) { 373 prns[i] = sGpsSvStatusCopy.sv_list[i].prn; 374 snrs[i] = sGpsSvStatusCopy.sv_list[i].snr; 375 elev[i] = sGpsSvStatusCopy.sv_list[i].elevation; 376 azim[i] = sGpsSvStatusCopy.sv_list[i].azimuth; 377 } 378 mask[0] = sGpsSvStatusCopy.ephemeris_mask; 379 mask[1] = sGpsSvStatusCopy.almanac_mask; 380 mask[2] = sGpsSvStatusCopy.used_in_fix_mask; 381 382 env->ReleaseIntArrayElements(prnArray, prns, 0); 383 env->ReleaseFloatArrayElements(snrArray, snrs, 0); 384 env->ReleaseFloatArrayElements(elevArray, elev, 0); 385 env->ReleaseFloatArrayElements(azumArray, azim, 0); 386 env->ReleaseIntArrayElements(maskArray, mask, 0); 387 return num_svs; 388} 389 390static jint android_location_GpsLocationProvider_read_nmea(JNIEnv* env, jobject obj, jint index, jbyteArray nmeaArray, jint buffer_size) 391{ 392 // this should only be called from within a call to reportNmea, so we don't need to lock here 393 394 jbyte* nmea = env->GetByteArrayElements(nmeaArray, 0); 395 396 int length = strlen(sNmeaBufferCopy[index].nmea); 397 if (length > buffer_size) 398 length = buffer_size; 399 memcpy(nmea, sNmeaBufferCopy[index].nmea, length); 400 401 env->ReleaseByteArrayElements(nmeaArray, nmea, 0); 402 return length; 403} 404 405static void android_location_GpsLocationProvider_inject_time(JNIEnv* env, jobject obj, jlong time, 406 jlong timeReference, jint uncertainty) 407{ 408 sGpsInterface->inject_time(time, timeReference, uncertainty); 409} 410 411static void android_location_GpsLocationProvider_inject_location(JNIEnv* env, jobject obj, 412 jdouble latitude, jdouble longitude, jfloat accuracy) 413{ 414 sGpsInterface->inject_location(latitude, longitude, accuracy); 415} 416 417static jboolean android_location_GpsLocationProvider_supports_xtra(JNIEnv* env, jobject obj) 418{ 419 if (!sGpsXtraInterface) { 420 sGpsXtraInterface = (const GpsXtraInterface*)sGpsInterface->get_extension(GPS_XTRA_INTERFACE); 421 if (sGpsXtraInterface) { 422 int result = sGpsXtraInterface->init(&sGpsXtraCallbacks); 423 if (result) { 424 sGpsXtraInterface = NULL; 425 } 426 } 427 } 428 429 return (sGpsXtraInterface != NULL); 430} 431 432static void android_location_GpsLocationProvider_inject_xtra_data(JNIEnv* env, jobject obj, 433 jbyteArray data, jint length) 434{ 435 jbyte* bytes = env->GetByteArrayElements(data, 0); 436 sGpsXtraInterface->inject_xtra_data((char *)bytes, length); 437 env->ReleaseByteArrayElements(data, bytes, 0); 438} 439 440static void android_location_GpsLocationProvider_agps_data_conn_open(JNIEnv* env, jobject obj, jstring apn) 441{ 442 if (!sAGpsInterface) { 443 sAGpsInterface = (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE); 444 } 445 if (sAGpsInterface) { 446 if (apn == NULL) { 447 jniThrowException(env, "java/lang/IllegalArgumentException", NULL); 448 return; 449 } 450 const char *apnStr = env->GetStringUTFChars(apn, NULL); 451 sAGpsInterface->data_conn_open(apnStr); 452 env->ReleaseStringUTFChars(apn, apnStr); 453 } 454} 455 456static void android_location_GpsLocationProvider_agps_data_conn_closed(JNIEnv* env, jobject obj) 457{ 458 if (!sAGpsInterface) { 459 sAGpsInterface = (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE); 460 } 461 if (sAGpsInterface) { 462 sAGpsInterface->data_conn_closed(); 463 } 464} 465 466static void android_location_GpsLocationProvider_agps_data_conn_failed(JNIEnv* env, jobject obj) 467{ 468 if (!sAGpsInterface) { 469 sAGpsInterface = (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE); 470 } 471 if (sAGpsInterface) { 472 sAGpsInterface->data_conn_failed(); 473 } 474} 475 476static void android_location_GpsLocationProvider_set_agps_server(JNIEnv* env, jobject obj, 477 jint type, jstring hostname, jint port) 478{ 479 if (!sAGpsInterface) { 480 sAGpsInterface = (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE); 481 } 482 if (sAGpsInterface) { 483 const char *c_hostname = env->GetStringUTFChars(hostname, NULL); 484 sAGpsInterface->set_server(type, c_hostname, port); 485 env->ReleaseStringUTFChars(hostname, c_hostname); 486 } 487} 488 489static void android_location_GpsLocationProvider_send_ni_response(JNIEnv* env, jobject obj, 490 jint notifId, jint response) 491{ 492 if (!sGpsNiInterface) 493 sGpsNiInterface = (const GpsNiInterface*)sGpsInterface->get_extension(GPS_NI_INTERFACE); 494 if (sGpsNiInterface) 495 sGpsNiInterface->respond(notifId, response); 496} 497 498static jstring android_location_GpsLocationProvider_get_internal_state(JNIEnv* env, jobject obj) 499{ 500 jstring result = NULL; 501 if (sGpsDebugInterface) { 502 const size_t maxLength = 2047; 503 char buffer[maxLength+1]; 504 size_t length = sGpsDebugInterface->get_internal_state(buffer, maxLength); 505 if (length > maxLength) length = maxLength; 506 buffer[length] = 0; 507 result = env->NewStringUTF(buffer); 508 } 509 return result; 510} 511 512static JNINativeMethod sMethods[] = { 513 /* name, signature, funcPtr */ 514 {"class_init_native", "()V", (void *)android_location_GpsLocationProvider_class_init_native}, 515 {"native_is_supported", "()Z", (void*)android_location_GpsLocationProvider_is_supported}, 516 {"native_init", "()Z", (void*)android_location_GpsLocationProvider_init}, 517 {"native_disable", "()V", (void*)android_location_GpsLocationProvider_disable}, 518 {"native_cleanup", "()V", (void*)android_location_GpsLocationProvider_cleanup}, 519 {"native_start", "(IZI)Z", (void*)android_location_GpsLocationProvider_start}, 520 {"native_stop", "()Z", (void*)android_location_GpsLocationProvider_stop}, 521 {"native_delete_aiding_data", "(I)V", (void*)android_location_GpsLocationProvider_delete_aiding_data}, 522 {"native_wait_for_event", "()V", (void*)android_location_GpsLocationProvider_wait_for_event}, 523 {"native_read_sv_status", "([I[F[F[F[I)I", (void*)android_location_GpsLocationProvider_read_sv_status}, 524 {"native_read_nmea", "(I[BI)I", (void*)android_location_GpsLocationProvider_read_nmea}, 525 {"native_inject_time", "(JJI)V", (void*)android_location_GpsLocationProvider_inject_time}, 526 {"native_inject_location", "(DDF)V", (void*)android_location_GpsLocationProvider_inject_location}, 527 {"native_supports_xtra", "()Z", (void*)android_location_GpsLocationProvider_supports_xtra}, 528 {"native_inject_xtra_data", "([BI)V", (void*)android_location_GpsLocationProvider_inject_xtra_data}, 529 {"native_agps_data_conn_open", "(Ljava/lang/String;)V", (void*)android_location_GpsLocationProvider_agps_data_conn_open}, 530 {"native_agps_data_conn_closed", "()V", (void*)android_location_GpsLocationProvider_agps_data_conn_closed}, 531 {"native_agps_data_conn_failed", "()V", (void*)android_location_GpsLocationProvider_agps_data_conn_failed}, 532 {"native_set_agps_server", "(ILjava/lang/String;I)V", (void*)android_location_GpsLocationProvider_set_agps_server}, 533 {"native_send_ni_response", "(II)V", (void*)android_location_GpsLocationProvider_send_ni_response}, 534 {"native_get_internal_state", "()Ljava/lang/String;", (void*)android_location_GpsLocationProvider_get_internal_state}, 535}; 536 537int register_android_location_GpsLocationProvider(JNIEnv* env) 538{ 539 return jniRegisterNativeMethods(env, "com/android/internal/location/GpsLocationProvider", sMethods, NELEM(sMethods)); 540} 541 542} /* namespace android */ 543