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 31 static pthread_mutex_t sEventMutex = PTHREAD_MUTEX_INITIALIZER; 32 static pthread_cond_t sEventCond = PTHREAD_COND_INITIALIZER; 33 static jmethodID method_reportLocation; 34 static jmethodID method_reportStatus; 35 static jmethodID method_reportSvStatus; 36 static jmethodID method_reportAGpsStatus; 37 static jmethodID method_reportNmea; 38 static jmethodID method_xtraDownloadRequest; 39 static jmethodID method_reportNiNotification; 40 41 static const GpsInterface* sGpsInterface = NULL; 42 static const GpsXtraInterface* sGpsXtraInterface = NULL; 43 static const AGpsInterface* sAGpsInterface = NULL; 44 static const GpsPrivacyInterface* sGpsPrivacyInterface = NULL; 45 static const GpsNiInterface* sGpsNiInterface = NULL; 46 static const GpsDebugInterface* sGpsDebugInterface = NULL; 47 48 // data written to by GPS callbacks 49 static GpsLocation sGpsLocation; 50 static GpsStatus sGpsStatus; 51 static GpsSvStatus sGpsSvStatus; 52 static AGpsStatus sAGpsStatus; 53 static GpsNiNotification sGpsNiNotification; 54 55 // buffer for NMEA data 56 #define NMEA_SENTENCE_LENGTH 100 57 #define NMEA_SENTENCE_COUNT 40 58 struct NmeaSentence { 59 GpsUtcTime timestamp; 60 char nmea[NMEA_SENTENCE_LENGTH]; 61 }; 62 static NmeaSentence sNmeaBuffer[NMEA_SENTENCE_COUNT]; 63 static 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 67 static GpsLocation sGpsLocationCopy; 68 static GpsStatus sGpsStatusCopy; 69 static GpsSvStatus sGpsSvStatusCopy; 70 static AGpsStatus sAGpsStatusCopy; 71 static NmeaSentence sNmeaBufferCopy[NMEA_SENTENCE_COUNT]; 72 static GpsNiNotification sGpsNiNotificationCopy; 73 74 enum 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 }; 84 static int sPendingCallbacks; 85 86 namespace android { 87 88 static 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 99 static 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 110 static 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 121 static 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 145 static 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 156 GpsCallbacks sGpsCallbacks = { 157 location_callback, 158 status_callback, 159 sv_status_callback, 160 nmea_callback 161 }; 162 163 static void 164 download_request_callback() 165 { 166 pthread_mutex_lock(&sEventMutex); 167 sPendingCallbacks |= kXtraDownloadRequest; 168 pthread_cond_signal(&sEventCond); 169 pthread_mutex_unlock(&sEventMutex); 170 } 171 172 static void 173 gps_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 186 GpsXtraCallbacks sGpsXtraCallbacks = { 187 download_request_callback, 188 }; 189 190 AGpsCallbacks sAGpsCallbacks = { 191 agps_status_callback, 192 }; 193 194 GpsNiCallbacks sGpsNiCallbacks = { 195 gps_ni_notify_callback, 196 }; 197 198 static 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 208 static jboolean android_location_GpsLocationProvider_is_supported(JNIEnv* env, jclass clazz) { 209 if (!sGpsInterface) 210 sGpsInterface = gps_get_interface(); 211 return (sGpsInterface != NULL); 212 } 213 214 static 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 243 static 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 257 static void android_location_GpsLocationProvider_cleanup(JNIEnv* env, jobject obj) 258 { 259 sGpsInterface->cleanup(); 260 } 261 262 static 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 273 static jboolean android_location_GpsLocationProvider_stop(JNIEnv* env, jobject obj) 274 { 275 return (sGpsInterface->stop() == 0); 276 } 277 278 static void android_location_GpsLocationProvider_delete_aiding_data(JNIEnv* env, jobject obj, jint flags) 279 { 280 sGpsInterface->delete_aiding_data(flags); 281 } 282 283 static 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 359 static 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 390 static 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 405 static 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 411 static 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 417 static 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 432 static 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 440 static 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 456 static 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 466 static 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 476 static 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 489 static 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 498 static 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 512 static 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 537 int 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