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#include "JNIHelp.h"
20#include "jni.h"
21#include "hardware_legacy/gps.h"
22#include "utils/Log.h"
23#include "utils/misc.h"
24
25#include <string.h>
26#include <pthread.h>
27
28
29static pthread_mutex_t sEventMutex = PTHREAD_MUTEX_INITIALIZER;
30static pthread_cond_t sEventCond = PTHREAD_COND_INITIALIZER;
31static jmethodID method_reportLocation;
32static jmethodID method_reportStatus;
33static jmethodID method_reportSvStatus;
34static jmethodID method_reportAGpsStatus;
35static jmethodID method_xtraDownloadRequest;
36
37static const GpsInterface* sGpsInterface = NULL;
38static const GpsXtraInterface* sGpsXtraInterface = NULL;
39static const AGpsInterface* sAGpsInterface = NULL;
40
41// data written to by GPS callbacks
42static GpsLocation  sGpsLocation;
43static GpsStatus    sGpsStatus;
44static GpsSvStatus  sGpsSvStatus;
45static AGpsStatus   sAGpsStatus;
46
47// a copy of the data shared by android_location_GpsLocationProvider_wait_for_event
48// and android_location_GpsLocationProvider_read_status
49static GpsLocation  sGpsLocationCopy;
50static GpsStatus    sGpsStatusCopy;
51static GpsSvStatus  sGpsSvStatusCopy;
52static AGpsStatus   sAGpsStatusCopy;
53
54enum CallbackType {
55    kLocation = 1,
56    kStatus = 2,
57    kSvStatus = 4,
58    kAGpsStatus = 8,
59    kXtraDownloadRequest = 16,
60    kDisableRequest = 32,
61};
62static int sPendingCallbacks;
63
64namespace android {
65
66static void location_callback(GpsLocation* location)
67{
68    pthread_mutex_lock(&sEventMutex);
69
70    sPendingCallbacks |= kLocation;
71    memcpy(&sGpsLocation, location, sizeof(sGpsLocation));
72
73    pthread_cond_signal(&sEventCond);
74    pthread_mutex_unlock(&sEventMutex);
75}
76
77static void status_callback(GpsStatus* status)
78{
79    pthread_mutex_lock(&sEventMutex);
80
81    sPendingCallbacks |= kStatus;
82    memcpy(&sGpsStatus, status, sizeof(sGpsStatus));
83
84    pthread_cond_signal(&sEventCond);
85    pthread_mutex_unlock(&sEventMutex);
86}
87
88static void sv_status_callback(GpsSvStatus* sv_status)
89{
90    pthread_mutex_lock(&sEventMutex);
91
92    sPendingCallbacks |= kSvStatus;
93    memcpy(&sGpsSvStatus, sv_status, sizeof(GpsSvStatus));
94
95    pthread_cond_signal(&sEventCond);
96    pthread_mutex_unlock(&sEventMutex);
97}
98
99static void agps_status_callback(AGpsStatus* agps_status)
100{
101    pthread_mutex_lock(&sEventMutex);
102
103    sPendingCallbacks |= kAGpsStatus;
104    memcpy(&sAGpsStatus, agps_status, sizeof(AGpsStatus));
105
106    pthread_cond_signal(&sEventCond);
107    pthread_mutex_unlock(&sEventMutex);
108}
109
110GpsCallbacks sGpsCallbacks = {
111    location_callback,
112    status_callback,
113    sv_status_callback,
114};
115
116static void
117download_request_callback()
118{
119    pthread_mutex_lock(&sEventMutex);
120    sPendingCallbacks |= kXtraDownloadRequest;
121    pthread_cond_signal(&sEventCond);
122    pthread_mutex_unlock(&sEventMutex);
123}
124
125GpsXtraCallbacks sGpsXtraCallbacks = {
126    download_request_callback,
127};
128
129AGpsCallbacks sAGpsCallbacks = {
130    agps_status_callback,
131};
132
133static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {
134    method_reportLocation = env->GetMethodID(clazz, "reportLocation", "(IDDDFFFJ)V");
135    method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V");
136    method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "()V");
137    method_reportAGpsStatus = env->GetMethodID(clazz, "reportAGpsStatus", "(II)V");
138    method_xtraDownloadRequest = env->GetMethodID(clazz, "xtraDownloadRequest", "()V");
139}
140
141static jboolean android_location_GpsLocationProvider_is_supported(JNIEnv* env, jclass clazz) {
142    if (!sGpsInterface)
143        sGpsInterface = gps_get_interface();
144    return (sGpsInterface != NULL);
145}
146
147static jboolean android_location_GpsLocationProvider_init(JNIEnv* env, jobject obj)
148{
149    if (!sGpsInterface)
150        sGpsInterface = gps_get_interface();
151    if (!sGpsInterface || sGpsInterface->init(&sGpsCallbacks) != 0)
152        return false;
153
154    if (!sAGpsInterface)
155        sAGpsInterface = (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE);
156    if (sAGpsInterface)
157        sAGpsInterface->init(&sAGpsCallbacks);
158    return true;
159}
160
161static void android_location_GpsLocationProvider_disable(JNIEnv* env, jobject obj)
162{
163    pthread_mutex_lock(&sEventMutex);
164    sPendingCallbacks |= kDisableRequest;
165    pthread_cond_signal(&sEventCond);
166    pthread_mutex_unlock(&sEventMutex);
167}
168
169static void android_location_GpsLocationProvider_cleanup(JNIEnv* env, jobject obj)
170{
171    sGpsInterface->cleanup();
172}
173
174static jboolean android_location_GpsLocationProvider_start(JNIEnv* env, jobject obj, jint positionMode,
175        jboolean singleFix, jint fixFrequency)
176{
177    int result = sGpsInterface->set_position_mode(positionMode, (singleFix ? 0 : fixFrequency));
178    if (result) {
179        return false;
180    }
181
182    return (sGpsInterface->start() == 0);
183}
184
185static jboolean android_location_GpsLocationProvider_stop(JNIEnv* env, jobject obj)
186{
187    return (sGpsInterface->stop() == 0);
188}
189
190static void android_location_GpsLocationProvider_delete_aiding_data(JNIEnv* env, jobject obj, jint flags)
191{
192    sGpsInterface->delete_aiding_data(flags);
193}
194
195static void android_location_GpsLocationProvider_wait_for_event(JNIEnv* env, jobject obj)
196{
197    pthread_mutex_lock(&sEventMutex);
198    pthread_cond_wait(&sEventCond, &sEventMutex);
199
200    // copy and clear the callback flags
201    int pendingCallbacks = sPendingCallbacks;
202    sPendingCallbacks = 0;
203
204    // copy everything and unlock the mutex before calling into Java code to avoid the possibility
205    // of timeouts in the GPS engine.
206    memcpy(&sGpsLocationCopy, &sGpsLocation, sizeof(sGpsLocationCopy));
207    memcpy(&sGpsStatusCopy, &sGpsStatus, sizeof(sGpsStatusCopy));
208    memcpy(&sGpsSvStatusCopy, &sGpsSvStatus, sizeof(sGpsSvStatusCopy));
209    memcpy(&sAGpsStatusCopy, &sAGpsStatus, sizeof(sAGpsStatusCopy));
210    pthread_mutex_unlock(&sEventMutex);
211
212    if (pendingCallbacks & kLocation) {
213        env->CallVoidMethod(obj, method_reportLocation, sGpsLocationCopy.flags,
214                (jdouble)sGpsLocationCopy.latitude, (jdouble)sGpsLocationCopy.longitude,
215                (jdouble)sGpsLocationCopy.altitude,
216                (jfloat)sGpsLocationCopy.speed, (jfloat)sGpsLocationCopy.bearing,
217                (jfloat)sGpsLocationCopy.accuracy, (jlong)sGpsLocationCopy.timestamp);
218    }
219    if (pendingCallbacks & kStatus) {
220        env->CallVoidMethod(obj, method_reportStatus, sGpsStatusCopy.status);
221    }
222    if (pendingCallbacks & kSvStatus) {
223        env->CallVoidMethod(obj, method_reportSvStatus);
224    }
225    if (pendingCallbacks & kAGpsStatus) {
226        env->CallVoidMethod(obj, method_reportAGpsStatus, sAGpsStatusCopy.type, sAGpsStatusCopy.status);
227    }
228    if (pendingCallbacks & kXtraDownloadRequest) {
229        env->CallVoidMethod(obj, method_xtraDownloadRequest);
230    }
231    if (pendingCallbacks & kDisableRequest) {
232        // don't need to do anything - we are just poking so wait_for_event will return.
233    }
234}
235
236static jint android_location_GpsLocationProvider_read_sv_status(JNIEnv* env, jobject obj,
237        jintArray prnArray, jfloatArray snrArray, jfloatArray elevArray, jfloatArray azumArray,
238        jintArray maskArray)
239{
240    // this should only be called from within a call to reportStatus, so we don't need to lock here
241
242    jint* prns = env->GetIntArrayElements(prnArray, 0);
243    jfloat* snrs = env->GetFloatArrayElements(snrArray, 0);
244    jfloat* elev = env->GetFloatArrayElements(elevArray, 0);
245    jfloat* azim = env->GetFloatArrayElements(azumArray, 0);
246    jint* mask = env->GetIntArrayElements(maskArray, 0);
247
248    int num_svs = sGpsSvStatusCopy.num_svs;
249    for (int i = 0; i < num_svs; i++) {
250        prns[i] = sGpsSvStatusCopy.sv_list[i].prn;
251        snrs[i] = sGpsSvStatusCopy.sv_list[i].snr;
252        elev[i] = sGpsSvStatusCopy.sv_list[i].elevation;
253        azim[i] = sGpsSvStatusCopy.sv_list[i].azimuth;
254    }
255    mask[0] = sGpsSvStatusCopy.ephemeris_mask;
256    mask[1] = sGpsSvStatusCopy.almanac_mask;
257    mask[2] = sGpsSvStatusCopy.used_in_fix_mask;
258
259    env->ReleaseIntArrayElements(prnArray, prns, 0);
260    env->ReleaseFloatArrayElements(snrArray, snrs, 0);
261    env->ReleaseFloatArrayElements(elevArray, elev, 0);
262    env->ReleaseFloatArrayElements(azumArray, azim, 0);
263    env->ReleaseIntArrayElements(maskArray, mask, 0);
264    return num_svs;
265}
266
267static void android_location_GpsLocationProvider_inject_time(JNIEnv* env, jobject obj, jlong time,
268        jlong timeReference, jint uncertainty)
269{
270    sGpsInterface->inject_time(time, timeReference, uncertainty);
271}
272
273static void android_location_GpsLocationProvider_inject_location(JNIEnv* env, jobject obj,
274        jdouble latitude, jdouble longitude, jfloat accuracy)
275{
276    sGpsInterface->inject_location(latitude, longitude, accuracy);
277}
278
279static jboolean android_location_GpsLocationProvider_supports_xtra(JNIEnv* env, jobject obj)
280{
281    if (!sGpsXtraInterface) {
282        sGpsXtraInterface = (const GpsXtraInterface*)sGpsInterface->get_extension(GPS_XTRA_INTERFACE);
283        if (sGpsXtraInterface) {
284            int result = sGpsXtraInterface->init(&sGpsXtraCallbacks);
285            if (result) {
286                sGpsXtraInterface = NULL;
287            }
288        }
289    }
290
291    return (sGpsXtraInterface != NULL);
292}
293
294static void android_location_GpsLocationProvider_inject_xtra_data(JNIEnv* env, jobject obj,
295        jbyteArray data, jint length)
296{
297    jbyte* bytes = env->GetByteArrayElements(data, 0);
298    sGpsXtraInterface->inject_xtra_data((char *)bytes, length);
299    env->ReleaseByteArrayElements(data, bytes, 0);
300}
301
302static void android_location_GpsLocationProvider_agps_data_conn_open(JNIEnv* env, jobject obj, jstring apn)
303{
304    if (!sAGpsInterface) {
305        sAGpsInterface = (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE);
306    }
307    if (sAGpsInterface) {
308        if (apn == NULL) {
309            jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
310            return;
311        }
312        const char *apnStr = env->GetStringUTFChars(apn, NULL);
313        sAGpsInterface->data_conn_open(apnStr);
314        env->ReleaseStringUTFChars(apn, apnStr);
315    }
316}
317
318static void android_location_GpsLocationProvider_agps_data_conn_closed(JNIEnv* env, jobject obj)
319{
320    if (!sAGpsInterface) {
321        sAGpsInterface = (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE);
322    }
323    if (sAGpsInterface) {
324        sAGpsInterface->data_conn_closed();
325    }
326}
327
328static void android_location_GpsLocationProvider_agps_data_conn_failed(JNIEnv* env, jobject obj)
329{
330    if (!sAGpsInterface) {
331        sAGpsInterface = (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE);
332    }
333    if (sAGpsInterface) {
334        sAGpsInterface->data_conn_failed();
335    }
336}
337
338static void android_location_GpsLocationProvider_set_agps_server(JNIEnv* env, jobject obj,
339        jint type, jstring hostname, jint port)
340{
341    if (!sAGpsInterface) {
342        sAGpsInterface = (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE);
343    }
344    if (sAGpsInterface) {
345        const char *c_hostname = env->GetStringUTFChars(hostname, NULL);
346        sAGpsInterface->set_server(type, c_hostname, port);
347        env->ReleaseStringUTFChars(hostname, c_hostname);
348    }
349}
350
351static JNINativeMethod sMethods[] = {
352     /* name, signature, funcPtr */
353    {"class_init_native", "()V", (void *)android_location_GpsLocationProvider_class_init_native},
354    {"native_is_supported", "()Z", (void*)android_location_GpsLocationProvider_is_supported},
355    {"native_init", "()Z", (void*)android_location_GpsLocationProvider_init},
356    {"native_disable", "()V", (void*)android_location_GpsLocationProvider_disable},
357    {"native_cleanup", "()V", (void*)android_location_GpsLocationProvider_cleanup},
358    {"native_start", "(IZI)Z", (void*)android_location_GpsLocationProvider_start},
359    {"native_stop", "()Z", (void*)android_location_GpsLocationProvider_stop},
360    {"native_delete_aiding_data", "(I)V", (void*)android_location_GpsLocationProvider_delete_aiding_data},
361    {"native_wait_for_event", "()V", (void*)android_location_GpsLocationProvider_wait_for_event},
362    {"native_read_sv_status", "([I[F[F[F[I)I", (void*)android_location_GpsLocationProvider_read_sv_status},
363    {"native_inject_time", "(JJI)V", (void*)android_location_GpsLocationProvider_inject_time},
364    {"native_inject_location", "(DDF)V", (void*)android_location_GpsLocationProvider_inject_location},
365    {"native_supports_xtra", "()Z", (void*)android_location_GpsLocationProvider_supports_xtra},
366    {"native_inject_xtra_data", "([BI)V", (void*)android_location_GpsLocationProvider_inject_xtra_data},
367    {"native_agps_data_conn_open", "(Ljava/lang/String;)V", (void*)android_location_GpsLocationProvider_agps_data_conn_open},
368    {"native_agps_data_conn_closed", "()V", (void*)android_location_GpsLocationProvider_agps_data_conn_closed},
369    {"native_agps_data_conn_failed", "()V", (void*)android_location_GpsLocationProvider_agps_data_conn_failed},
370    {"native_set_agps_server", "(ILjava/lang/String;I)V", (void*)android_location_GpsLocationProvider_set_agps_server},
371};
372
373int register_android_location_GpsLocationProvider(JNIEnv* env)
374{
375    return jniRegisterNativeMethods(env, "com/android/internal/location/GpsLocationProvider", sMethods, NELEM(sMethods));
376}
377
378} /* namespace android */
379