1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtPositioning module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39
40 #include <QDateTime>
41 #include <QDebug>
42 #include <QMap>
43 #include <QRandomGenerator>
44 #include <QtGlobal>
45 #include <QtCore/private/qjnihelpers_p.h>
46 #include <android/log.h>
47 #include <QGeoPositionInfo>
48 #include "qgeopositioninfosource_android_p.h"
49 #include "qgeosatelliteinfosource_android_p.h"
50
51 #include "jnipositioning.h"
52
53 static JavaVM *javaVM = nullptr;
54 static jclass positioningClass;
55
56 static jmethodID providerListMethodId;
57 static jmethodID lastKnownPositionMethodId;
58 static jmethodID startUpdatesMethodId;
59 static jmethodID stopUpdatesMethodId;
60 static jmethodID requestUpdateMethodId;
61 static jmethodID startSatelliteUpdatesMethodId;
62
63 static const char logTag[] = "QtPositioning";
64 static const char classErrorMsg[] = "Can't find class \"%s\"";
65 static const char methodErrorMsg[] = "Can't find method \"%s%s\"";
66
67 namespace AndroidPositioning {
68 typedef QMap<int, QGeoPositionInfoSourceAndroid * > PositionSourceMap;
69 typedef QMap<int, QGeoSatelliteInfoSourceAndroid * > SatelliteSourceMap;
70
71 Q_GLOBAL_STATIC(PositionSourceMap, idToPosSource)
72
73 Q_GLOBAL_STATIC(SatelliteSourceMap, idToSatSource)
74
75 struct AttachedJNIEnv
76 {
AttachedJNIEnvAndroidPositioning::AttachedJNIEnv77 AttachedJNIEnv()
78 {
79 attached = false;
80 if (javaVM && javaVM->GetEnv(reinterpret_cast<void**>(&jniEnv), JNI_VERSION_1_6) < 0) {
81 if (javaVM->AttachCurrentThread(&jniEnv, nullptr) < 0) {
82 __android_log_print(ANDROID_LOG_ERROR, logTag, "AttachCurrentThread failed");
83 jniEnv = nullptr;
84 return;
85 }
86 attached = true;
87 }
88 }
89
~AttachedJNIEnvAndroidPositioning::AttachedJNIEnv90 ~AttachedJNIEnv()
91 {
92 if (attached)
93 javaVM->DetachCurrentThread();
94 }
95 bool attached;
96 JNIEnv *jniEnv;
97 };
98
registerPositionInfoSource(QObject * obj)99 int registerPositionInfoSource(QObject *obj)
100 {
101 static bool firstInit = true;
102 if (firstInit) {
103 firstInit = false;
104 }
105
106 int key = -1;
107 if (obj->inherits("QGeoPositionInfoSource")) {
108 QGeoPositionInfoSourceAndroid *src = qobject_cast<QGeoPositionInfoSourceAndroid *>(obj);
109 Q_ASSERT(src);
110 do {
111 key = qAbs(int(QRandomGenerator::global()->generate()));
112 } while (idToPosSource()->contains(key));
113
114 idToPosSource()->insert(key, src);
115 } else if (obj->inherits("QGeoSatelliteInfoSource")) {
116 QGeoSatelliteInfoSourceAndroid *src = qobject_cast<QGeoSatelliteInfoSourceAndroid *>(obj);
117 Q_ASSERT(src);
118 do {
119 key = qAbs(int(QRandomGenerator::global()->generate()));
120 } while (idToSatSource()->contains(key));
121
122 idToSatSource()->insert(key, src);
123 }
124
125 return key;
126 }
127
unregisterPositionInfoSource(int key)128 void unregisterPositionInfoSource(int key)
129 {
130 idToPosSource()->remove(key);
131 idToSatSource()->remove(key);
132 }
133
134 enum PositionProvider
135 {
136 PROVIDER_GPS = 0,
137 PROVIDER_NETWORK = 1,
138 PROVIDER_PASSIVE = 2
139 };
140
141
availableProviders()142 QGeoPositionInfoSource::PositioningMethods availableProviders()
143 {
144 QGeoPositionInfoSource::PositioningMethods ret = QGeoPositionInfoSource::NoPositioningMethods;
145 AttachedJNIEnv env;
146 if (!env.jniEnv)
147 return ret;
148 jintArray jProviders = static_cast<jintArray>(env.jniEnv->CallStaticObjectMethod(
149 positioningClass, providerListMethodId));
150 jint *providers = env.jniEnv->GetIntArrayElements(jProviders, nullptr);
151 const int size = env.jniEnv->GetArrayLength(jProviders);
152 for (int i = 0; i < size; i++) {
153 switch (providers[i]) {
154 case PROVIDER_GPS:
155 ret |= QGeoPositionInfoSource::SatellitePositioningMethods;
156 break;
157 case PROVIDER_NETWORK:
158 ret |= QGeoPositionInfoSource::NonSatellitePositioningMethods;
159 break;
160 case PROVIDER_PASSIVE:
161 //we ignore as Qt doesn't have interface for it right now
162 break;
163 default:
164 __android_log_print(ANDROID_LOG_INFO, logTag, "Unknown positioningMethod");
165 }
166 }
167
168 env.jniEnv->ReleaseIntArrayElements(jProviders, providers, 0);
169 env.jniEnv->DeleteLocalRef(jProviders);
170
171 return ret;
172 }
173
174 //caching originally taken from corelib/kernel/qjni.cpp
175 typedef QHash<QByteArray, jmethodID> JMethodIDHash;
Q_GLOBAL_STATIC(JMethodIDHash,cachedMethodID)176 Q_GLOBAL_STATIC(JMethodIDHash, cachedMethodID)
177
178 static jmethodID getCachedMethodID(JNIEnv *env,
179 jclass clazz,
180 const char *name,
181 const char *sig)
182 {
183 jmethodID id = nullptr;
184 uint offset_name = qstrlen(name);
185 uint offset_signal = qstrlen(sig);
186 QByteArray key(int(offset_name + offset_signal), Qt::Uninitialized);
187 memcpy(key.data(), name, offset_name);
188 memcpy(key.data()+offset_name, sig, offset_signal);
189 QHash<QByteArray, jmethodID>::iterator it = cachedMethodID->find(key);
190 if (it == cachedMethodID->end()) {
191 id = env->GetMethodID(clazz, name, sig);
192 if (env->ExceptionCheck()) {
193 id = nullptr;
194 #ifdef QT_DEBUG
195 env->ExceptionDescribe();
196 #endif // QT_DEBUG
197 env->ExceptionClear();
198 }
199
200 cachedMethodID->insert(key, id);
201 } else {
202 id = it.value();
203 }
204 return id;
205 }
206
positionInfoFromJavaLocation(JNIEnv * jniEnv,const jobject & location)207 QGeoPositionInfo positionInfoFromJavaLocation(JNIEnv * jniEnv, const jobject &location)
208 {
209 QGeoPositionInfo info;
210 jclass thisClass = jniEnv->GetObjectClass(location);
211 if (!thisClass)
212 return QGeoPositionInfo();
213
214 jmethodID mid = getCachedMethodID(jniEnv, thisClass, "getLatitude", "()D");
215 jdouble latitude = jniEnv->CallDoubleMethod(location, mid);
216 mid = getCachedMethodID(jniEnv, thisClass, "getLongitude", "()D");
217 jdouble longitude = jniEnv->CallDoubleMethod(location, mid);
218 QGeoCoordinate coordinate(latitude, longitude);
219
220 //altitude
221 mid = getCachedMethodID(jniEnv, thisClass, "hasAltitude", "()Z");
222 jboolean attributeExists = jniEnv->CallBooleanMethod(location, mid);
223 if (attributeExists) {
224 mid = getCachedMethodID(jniEnv, thisClass, "getAltitude", "()D");
225 jdouble value = jniEnv->CallDoubleMethod(location, mid);
226 if (value != 0.0)
227 {
228 coordinate.setAltitude(value);
229 }
230 }
231
232 info.setCoordinate(coordinate);
233
234 //time stamp
235 mid = getCachedMethodID(jniEnv, thisClass, "getTime", "()J");
236 jlong timestamp = jniEnv->CallLongMethod(location, mid);
237 info.setTimestamp(QDateTime::fromMSecsSinceEpoch(timestamp, Qt::UTC));
238
239 //horizontal accuracy
240 mid = getCachedMethodID(jniEnv, thisClass, "hasAccuracy", "()Z");
241 attributeExists = jniEnv->CallBooleanMethod(location, mid);
242 if (attributeExists) {
243 mid = getCachedMethodID(jniEnv, thisClass, "getAccuracy", "()F");
244 jfloat accuracy = jniEnv->CallFloatMethod(location, mid);
245 if (accuracy != 0.0)
246 {
247 info.setAttribute(QGeoPositionInfo::HorizontalAccuracy, qreal(accuracy));
248 }
249 }
250
251 //vertical accuracy
252 mid = getCachedMethodID(jniEnv, thisClass, "hasVerticalAccuracy", "()Z");
253 if (mid) {
254 attributeExists = jniEnv->CallBooleanMethod(location, mid);
255 if (attributeExists) {
256 mid = getCachedMethodID(jniEnv, thisClass, "getVerticalAccuracyMeters", "()F");
257 if (mid) {
258 jfloat accuracy = jniEnv->CallFloatMethod(location, mid);
259 if (accuracy != 0.0)
260 {
261 info.setAttribute(QGeoPositionInfo::VerticalAccuracy, qreal(accuracy));
262 }
263 }
264 }
265 }
266
267 if (!mid)
268 jniEnv->ExceptionClear();
269
270 //ground speed
271 mid = getCachedMethodID(jniEnv, thisClass, "hasSpeed", "()Z");
272 attributeExists = jniEnv->CallBooleanMethod(location, mid);
273 if (attributeExists) {
274 mid = getCachedMethodID(jniEnv, thisClass, "getSpeed", "()F");
275 jfloat speed = jniEnv->CallFloatMethod(location, mid);
276 if (speed != 0)
277 {
278 info.setAttribute(QGeoPositionInfo::GroundSpeed, qreal(speed));
279 }
280 }
281
282 //bearing
283 mid = getCachedMethodID(jniEnv, thisClass, "hasBearing", "()Z");
284 attributeExists = jniEnv->CallBooleanMethod(location, mid);
285 if (attributeExists) {
286 mid = getCachedMethodID(jniEnv, thisClass, "getBearing", "()F");
287 jfloat bearing = jniEnv->CallFloatMethod(location, mid);
288 if (bearing != 0.0)
289 {
290 info.setAttribute(QGeoPositionInfo::Direction, qreal(bearing));
291 }
292 }
293
294 jniEnv->DeleteLocalRef(thisClass);
295 return info;
296 }
297
satelliteInfoFromJavaLocation(JNIEnv * jniEnv,jobjectArray satellites,QList<QGeoSatelliteInfo> * usedInFix)298 QList<QGeoSatelliteInfo> satelliteInfoFromJavaLocation(JNIEnv *jniEnv,
299 jobjectArray satellites,
300 QList<QGeoSatelliteInfo>* usedInFix)
301 {
302 QList<QGeoSatelliteInfo> sats;
303 jsize length = jniEnv->GetArrayLength(satellites);
304 for (int i = 0; i<length; i++) {
305 jobject element = jniEnv->GetObjectArrayElement(satellites, i);
306 if (jniEnv->ExceptionOccurred()) {
307 qWarning() << "Cannot process all satellite data due to exception.";
308 break;
309 }
310
311 jclass thisClass = jniEnv->GetObjectClass(element);
312 if (!thisClass)
313 continue;
314
315 QGeoSatelliteInfo info;
316
317 //signal strength
318 jmethodID mid = getCachedMethodID(jniEnv, thisClass, "getSnr", "()F");
319 jfloat snr = jniEnv->CallFloatMethod(element, mid);
320 info.setSignalStrength(int(snr));
321
322 //ignore any satellite with no signal whatsoever
323 if (qFuzzyIsNull(snr))
324 continue;
325
326 //prn
327 mid = getCachedMethodID(jniEnv, thisClass, "getPrn", "()I");
328 jint prn = jniEnv->CallIntMethod(element, mid);
329 info.setSatelliteIdentifier(prn);
330
331 if (prn >= 1 && prn <= 32)
332 info.setSatelliteSystem(QGeoSatelliteInfo::GPS);
333 else if (prn >= 65 && prn <= 96)
334 info.setSatelliteSystem(QGeoSatelliteInfo::GLONASS);
335
336 //azimuth
337 mid = getCachedMethodID(jniEnv, thisClass, "getAzimuth", "()F");
338 jfloat azimuth = jniEnv->CallFloatMethod(element, mid);
339 info.setAttribute(QGeoSatelliteInfo::Azimuth, qreal(azimuth));
340
341 //elevation
342 mid = getCachedMethodID(jniEnv, thisClass, "getElevation", "()F");
343 jfloat elevation = jniEnv->CallFloatMethod(element, mid);
344 info.setAttribute(QGeoSatelliteInfo::Elevation, qreal(elevation));
345
346 //used in a fix
347 mid = getCachedMethodID(jniEnv, thisClass, "usedInFix", "()Z");
348 jboolean inFix = jniEnv->CallBooleanMethod(element, mid);
349
350 sats.append(info);
351
352 if (inFix)
353 usedInFix->append(info);
354
355 jniEnv->DeleteLocalRef(thisClass);
356 jniEnv->DeleteLocalRef(element);
357 }
358
359 return sats;
360 }
361
lastKnownPosition(bool fromSatellitePositioningMethodsOnly)362 QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly)
363 {
364 AttachedJNIEnv env;
365 if (!env.jniEnv)
366 return QGeoPositionInfo();
367
368 if (!requestionPositioningPermissions(env.jniEnv))
369 return {};
370
371 jobject location = env.jniEnv->CallStaticObjectMethod(positioningClass,
372 lastKnownPositionMethodId,
373 fromSatellitePositioningMethodsOnly);
374 if (location == nullptr)
375 return QGeoPositionInfo();
376
377 const QGeoPositionInfo info = positionInfoFromJavaLocation(env.jniEnv, location);
378 env.jniEnv->DeleteLocalRef(location);
379
380 return info;
381 }
382
positioningMethodToInt(QGeoPositionInfoSource::PositioningMethods m)383 inline int positioningMethodToInt(QGeoPositionInfoSource::PositioningMethods m)
384 {
385 int providerSelection = 0;
386 if (m & QGeoPositionInfoSource::SatellitePositioningMethods)
387 providerSelection |= 1;
388 if (m & QGeoPositionInfoSource::NonSatellitePositioningMethods)
389 providerSelection |= 2;
390
391 return providerSelection;
392 }
393
startUpdates(int androidClassKey)394 QGeoPositionInfoSource::Error startUpdates(int androidClassKey)
395 {
396 AttachedJNIEnv env;
397 if (!env.jniEnv)
398 return QGeoPositionInfoSource::UnknownSourceError;
399
400 QGeoPositionInfoSourceAndroid *source = AndroidPositioning::idToPosSource()->value(androidClassKey);
401
402 if (source) {
403 if (!requestionPositioningPermissions(env.jniEnv))
404 return QGeoPositionInfoSource::AccessError;
405
406 int errorCode = env.jniEnv->CallStaticIntMethod(positioningClass, startUpdatesMethodId,
407 androidClassKey,
408 positioningMethodToInt(source->preferredPositioningMethods()),
409 source->updateInterval());
410 switch (errorCode) {
411 case 0:
412 case 1:
413 case 2:
414 case 3:
415 return static_cast<QGeoPositionInfoSource::Error>(errorCode);
416 default:
417 break;
418 }
419 }
420
421 return QGeoPositionInfoSource::UnknownSourceError;
422 }
423
424 //used for stopping regular and single updates
stopUpdates(int androidClassKey)425 void stopUpdates(int androidClassKey)
426 {
427 AttachedJNIEnv env;
428 if (!env.jniEnv)
429 return;
430
431 env.jniEnv->CallStaticVoidMethod(positioningClass, stopUpdatesMethodId, androidClassKey);
432 }
433
requestUpdate(int androidClassKey)434 QGeoPositionInfoSource::Error requestUpdate(int androidClassKey)
435 {
436 AttachedJNIEnv env;
437 if (!env.jniEnv)
438 return QGeoPositionInfoSource::UnknownSourceError;
439
440 QGeoPositionInfoSourceAndroid *source = AndroidPositioning::idToPosSource()->value(androidClassKey);
441
442 if (source) {
443 if (!requestionPositioningPermissions(env.jniEnv))
444 return QGeoPositionInfoSource::AccessError;
445
446 int errorCode = env.jniEnv->CallStaticIntMethod(positioningClass, requestUpdateMethodId,
447 androidClassKey,
448 positioningMethodToInt(source->preferredPositioningMethods()));
449 switch (errorCode) {
450 case 0:
451 case 1:
452 case 2:
453 case 3:
454 return static_cast<QGeoPositionInfoSource::Error>(errorCode);
455 default:
456 break;
457 }
458 }
459 return QGeoPositionInfoSource::UnknownSourceError;
460 }
461
startSatelliteUpdates(int androidClassKey,bool isSingleRequest,int requestTimeout)462 QGeoSatelliteInfoSource::Error startSatelliteUpdates(int androidClassKey, bool isSingleRequest, int requestTimeout)
463 {
464 AttachedJNIEnv env;
465 if (!env.jniEnv)
466 return QGeoSatelliteInfoSource::UnknownSourceError;
467
468 QGeoSatelliteInfoSourceAndroid *source = AndroidPositioning::idToSatSource()->value(androidClassKey);
469
470 if (source) {
471 if (!requestionPositioningPermissions(env.jniEnv))
472 return QGeoSatelliteInfoSource::AccessError;
473
474 int interval = source->updateInterval();
475 if (isSingleRequest)
476 interval = requestTimeout;
477 int errorCode = env.jniEnv->CallStaticIntMethod(positioningClass, startSatelliteUpdatesMethodId,
478 androidClassKey,
479 interval, isSingleRequest);
480 switch (errorCode) {
481 case -1:
482 case 0:
483 case 1:
484 case 2:
485 return static_cast<QGeoSatelliteInfoSource::Error>(errorCode);
486 default:
487 qWarning() << "startSatelliteUpdates: Unknown error code " << errorCode;
488 break;
489 }
490 }
491 return QGeoSatelliteInfoSource::UnknownSourceError;
492 }
493
requestionPositioningPermissions(JNIEnv * env)494 bool requestionPositioningPermissions(JNIEnv *env)
495 {
496 using namespace QtAndroidPrivate;
497
498 if (androidSdkVersion() < 23)
499 return true;
500
501 // Android v23+ requires runtime permission check and requests
502 QString permission(QLatin1String("android.permission.ACCESS_FINE_LOCATION"));
503
504 if (checkPermission(permission) == PermissionsResult::Denied) {
505 const QHash<QString, PermissionsResult> results =
506 requestPermissionsSync(env, QStringList() << permission);
507 if (!results.contains(permission) || results[permission] == PermissionsResult::Denied) {
508 qWarning() << "Position data not available due to missing permission " << permission;
509 return false;
510 }
511 }
512
513 return true;
514 }
515 }
516
positionUpdated(JNIEnv * env,jobject,jobject location,jint androidClassKey,jboolean isSingleUpdate)517 static void positionUpdated(JNIEnv *env, jobject /*thiz*/, jobject location, jint androidClassKey, jboolean isSingleUpdate)
518 {
519 QGeoPositionInfo info = AndroidPositioning::positionInfoFromJavaLocation(env, location);
520
521 QGeoPositionInfoSourceAndroid *source = AndroidPositioning::idToPosSource()->value(androidClassKey);
522 if (!source) {
523 qWarning("positionUpdated: source == 0");
524 return;
525 }
526
527 //we need to invoke indirectly as the Looper thread is likely to be not the same thread
528 if (!isSingleUpdate)
529 QMetaObject::invokeMethod(source, "processPositionUpdate", Qt::AutoConnection,
530 Q_ARG(QGeoPositionInfo, info));
531 else
532 QMetaObject::invokeMethod(source, "processSinglePositionUpdate", Qt::AutoConnection,
533 Q_ARG(QGeoPositionInfo, info));
534 }
535
locationProvidersDisabled(JNIEnv * env,jobject,jint androidClassKey)536 static void locationProvidersDisabled(JNIEnv *env, jobject /*thiz*/, jint androidClassKey)
537 {
538 Q_UNUSED(env);
539 QObject *source = AndroidPositioning::idToPosSource()->value(androidClassKey);
540 if (!source)
541 source = AndroidPositioning::idToSatSource()->value(androidClassKey);
542 if (!source) {
543 qWarning("locationProvidersDisabled: source == 0");
544 return;
545 }
546
547 QMetaObject::invokeMethod(source, "locationProviderDisabled", Qt::AutoConnection);
548 }
549
locationProvidersChanged(JNIEnv * env,jobject,jint androidClassKey)550 static void locationProvidersChanged(JNIEnv *env, jobject /*thiz*/, jint androidClassKey)
551 {
552 Q_UNUSED(env);
553 QObject *source = AndroidPositioning::idToPosSource()->value(androidClassKey);
554 if (!source) {
555 qWarning("locationProvidersChanged: source == 0");
556 return;
557 }
558
559 QMetaObject::invokeMethod(source, "locationProvidersChanged", Qt::AutoConnection);
560 }
561
satelliteUpdated(JNIEnv * env,jobject,jobjectArray satellites,jint androidClassKey,jboolean isSingleUpdate)562 static void satelliteUpdated(JNIEnv *env, jobject /*thiz*/, jobjectArray satellites, jint androidClassKey, jboolean isSingleUpdate)
563 {
564 QList<QGeoSatelliteInfo> inUse;
565 QList<QGeoSatelliteInfo> sats = AndroidPositioning::satelliteInfoFromJavaLocation(env, satellites, &inUse);
566
567 QGeoSatelliteInfoSourceAndroid *source = AndroidPositioning::idToSatSource()->value(androidClassKey);
568 if (!source) {
569 qWarning("satelliteUpdated: source == 0");
570 return;
571 }
572
573 QMetaObject::invokeMethod(source, "processSatelliteUpdateInView", Qt::AutoConnection,
574 Q_ARG(QList<QGeoSatelliteInfo>, sats), Q_ARG(bool, isSingleUpdate));
575
576 QMetaObject::invokeMethod(source, "processSatelliteUpdateInUse", Qt::AutoConnection,
577 Q_ARG(QList<QGeoSatelliteInfo>, inUse), Q_ARG(bool, isSingleUpdate));
578 }
579
580
581 #define FIND_AND_CHECK_CLASS(CLASS_NAME) \
582 clazz = env->FindClass(CLASS_NAME); \
583 if (!clazz) { \
584 __android_log_print(ANDROID_LOG_FATAL, logTag, classErrorMsg, CLASS_NAME); \
585 return JNI_FALSE; \
586 }
587
588 #define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE) \
589 VAR = env->GetStaticMethodID(CLASS, METHOD_NAME, METHOD_SIGNATURE); \
590 if (!VAR) { \
591 __android_log_print(ANDROID_LOG_FATAL, logTag, methodErrorMsg, METHOD_NAME, METHOD_SIGNATURE); \
592 return JNI_FALSE; \
593 }
594
595 static JNINativeMethod methods[] = {
596 {"positionUpdated", "(Landroid/location/Location;IZ)V", (void *)positionUpdated},
597 {"locationProvidersDisabled", "(I)V", (void *) locationProvidersDisabled},
598 {"satelliteUpdated", "([Landroid/location/GpsSatellite;IZ)V", (void *)satelliteUpdated},
599 {"locationProvidersChanged", "(I)V", (void *) locationProvidersChanged}
600 };
601
registerNatives(JNIEnv * env)602 static bool registerNatives(JNIEnv *env)
603 {
604 jclass clazz;
605 FIND_AND_CHECK_CLASS("org/qtproject/qt5/android/positioning/QtPositioning");
606 positioningClass = static_cast<jclass>(env->NewGlobalRef(clazz));
607
608 if (env->RegisterNatives(positioningClass, methods, sizeof(methods) / sizeof(methods[0])) < 0) {
609 __android_log_print(ANDROID_LOG_FATAL, logTag, "RegisterNatives failed");
610 return JNI_FALSE;
611 }
612
613 GET_AND_CHECK_STATIC_METHOD(providerListMethodId, positioningClass, "providerList", "()[I");
614 GET_AND_CHECK_STATIC_METHOD(lastKnownPositionMethodId, positioningClass, "lastKnownPosition", "(Z)Landroid/location/Location;");
615 GET_AND_CHECK_STATIC_METHOD(startUpdatesMethodId, positioningClass, "startUpdates", "(III)I");
616 GET_AND_CHECK_STATIC_METHOD(stopUpdatesMethodId, positioningClass, "stopUpdates", "(I)V");
617 GET_AND_CHECK_STATIC_METHOD(requestUpdateMethodId, positioningClass, "requestUpdate", "(II)I");
618 GET_AND_CHECK_STATIC_METHOD(startSatelliteUpdatesMethodId, positioningClass, "startSatelliteUpdates", "(IIZ)I");
619
620 return true;
621 }
622
JNI_OnLoad(JavaVM * vm,void *)623 Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /*reserved*/)
624 {
625 static bool initialized = false;
626 if (initialized)
627 return JNI_VERSION_1_6;
628 initialized = true;
629
630 typedef union {
631 JNIEnv *nativeEnvironment;
632 void *venv;
633 } UnionJNIEnvToVoid;
634
635 __android_log_print(ANDROID_LOG_INFO, logTag, "Positioning start");
636 UnionJNIEnvToVoid uenv;
637 uenv.venv = nullptr;
638 javaVM = nullptr;
639
640 if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_6) != JNI_OK) {
641 __android_log_print(ANDROID_LOG_FATAL, logTag, "GetEnv failed");
642 return -1;
643 }
644 JNIEnv *env = uenv.nativeEnvironment;
645 if (!registerNatives(env)) {
646 __android_log_print(ANDROID_LOG_FATAL, logTag, "registerNatives failed");
647 return -1;
648 }
649
650 javaVM = vm;
651 return JNI_VERSION_1_6;
652 }
653
654