Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
jnipositioning.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
7#include <QtPositioning/QGeoPositionInfo>
8#include <QtCore/QDateTime>
9#include <QtCore/QMap>
10#include <QtCore/QRandomGenerator>
11#include <QtCore/QJniEnvironment>
12#include <QtCore/QJniObject>
13#include <QtCore/QLoggingCategory>
14#include <QtCore/QPermission>
15#include <QtCore/QCoreApplication>
16#include <QtCore/QTimeZone>
17#include <QtCore/QSet>
18#include <android/log.h>
19
20Q_DECLARE_JNI_CLASS(QtPositioning, "org/qtproject/qt/android/positioning/QtPositioning")
21Q_DECLARE_JNI_CLASS(GnssStatus, "android/location/GnssStatus")
22Q_DECLARE_JNI_CLASS(Location, "android/location/Location")
23
24using namespace Qt::StringLiterals;
25
26template<typename T>
28{
29public:
32 {
33 if (m_classRef) {
34 QJniEnvironment env;
35 if (env.jniEnv())
36 env->DeleteGlobalRef(m_classRef);
37 }
38 }
39
40 bool init()
41 {
42 QJniEnvironment env;
43 if (env.jniEnv()) {
44 if (m_classRef) {
45 env->DeleteGlobalRef(m_classRef);
46 m_classRef = nullptr;
47 }
48
49 m_classRef = env.findClass<T>(); // it returns global ref!
50 }
51 return m_classRef != nullptr;
52 }
53
54 jclass operator()() { return m_classRef; }
55
56private:
57 jclass m_classRef = nullptr;
58};
59
61
68
69static const char logTag[] = "qt.positioning.android";
70static const char methodErrorMsg[] = "Can't find method \"%s%s\"";
71
72Q_LOGGING_CATEGORY(lcPositioning, logTag)
73
74namespace {
75
76/*!
77 \internal
78 This class encapsulates satellite system types, as defined by Android
79 GnssStatus API. Initialize during JNI_OnLoad() by the init() method, from
80 the Java side, rather than hard-coding.
81*/
82class ConstellationMapper
83{
84public:
85 static bool init()
86 {
87 m_gnssStatusObject = QJniEnvironment().findClass<QtJniTypes::GnssStatus>();
88 if (!m_gnssStatusObject)
89 return false;
90
91 const auto sdkVersion = QNativeInterface::QAndroidApplication::sdkVersion();
92 if (sdkVersion >= 29) {
93 m_irnssTypeId = QJniObject::getStaticField<jint>(m_gnssStatusObject,
94 "CONSTELLATION_IRNSS");
95 }
96 return true;
97 }
98
99 static QGeoSatelliteInfo::SatelliteSystem toSatelliteSystem(int constellationType)
100 {
101 if (!m_gnssStatusObject)
102 return QGeoSatelliteInfo::Undefined;
103
104 static const int gps =
105 QJniObject::getStaticField<jint>(m_gnssStatusObject, "CONSTELLATION_GPS");
106 static const int glonass =
107 QJniObject::getStaticField<jint>(m_gnssStatusObject, "CONSTELLATION_GLONASS");
108 static const int galileo =
109 QJniObject::getStaticField<jint>(m_gnssStatusObject, "CONSTELLATION_GALILEO");
110 static const int beidou =
111 QJniObject::getStaticField<jint>(m_gnssStatusObject, "CONSTELLATION_BEIDOU");
112 static const int qzss =
113 QJniObject::getStaticField<jint>(m_gnssStatusObject, "CONSTELLATION_QZSS");
114 static const int sbas =
115 QJniObject::getStaticField<jint>(m_gnssStatusObject, "CONSTELLATION_SBAS");
116
117 if (constellationType == gps) {
118 return QGeoSatelliteInfo::GPS;
119 } else if (constellationType == glonass) {
120 return QGeoSatelliteInfo::GLONASS;
121 } else if (constellationType == galileo) {
122 return QGeoSatelliteInfo::GALILEO;
123 } else if (constellationType == beidou) {
124 return QGeoSatelliteInfo::BEIDOU;
125 } else if (constellationType == qzss){
126 return QGeoSatelliteInfo::QZSS;
127 } else if (constellationType == sbas) {
128 return QGeoSatelliteInfo::SBAS;
129 } else if (m_irnssTypeId > 0 && constellationType == m_irnssTypeId) {
130 return QGeoSatelliteInfo::IRNSS;
131 } else {
132 qCWarning(lcPositioning) << "Unknown satellite system" << constellationType;
133 return QGeoSatelliteInfo::Undefined;
134 }
135 }
136
137private:
138 static jclass m_gnssStatusObject;
139 static int m_irnssTypeId; // API level >= 29!
140};
141
142jclass ConstellationMapper::m_gnssStatusObject = nullptr;
143int ConstellationMapper::m_irnssTypeId = 0;
144
145} // anonymous namespace
146
150
152
154
156 {
157 int key = -1;
158 if (obj->inherits("QGeoPositionInfoSource")) {
160 Q_ASSERT(src);
161 do {
163 } while (idToPosSource()->contains(key));
164
166 } else if (obj->inherits("QGeoSatelliteInfoSource")) {
168 Q_ASSERT(src);
169 do {
171 } while (idToSatSource()->contains(key));
172
174 }
175
176 return key;
177 }
178
187
194
195
197 {
198 QGeoPositionInfoSource::PositioningMethods ret = QGeoPositionInfoSource::NoPositioningMethods;
199 QJniEnvironment env;
200 if (!env.jniEnv())
201 return ret;
202 QJniObject jniProvidersObj =
203 QJniObject::callStaticMethod<jobject>(positioningClass(), providerListMethodId);
204 jintArray jProviders = jniProvidersObj.object<jintArray>();
205 if (!jProviders) {
206 // Work-around for QTBUG-116645
207 __android_log_print(ANDROID_LOG_INFO, logTag, "Got null providers array!");
208 return ret;
209 }
210 jint *providers = env->GetIntArrayElements(jProviders, nullptr);
211 const int size = env->GetArrayLength(jProviders);
212 for (int i = 0; i < size; i++) {
213 switch (providers[i]) {
214 case PROVIDER_GPS:
215 ret |= QGeoPositionInfoSource::SatellitePositioningMethods;
216 break;
217 case PROVIDER_NETWORK:
218 ret |= QGeoPositionInfoSource::NonSatellitePositioningMethods;
219 break;
220 case PROVIDER_PASSIVE:
221 //we ignore as Qt doesn't have interface for it right now
222 break;
223 default:
224 __android_log_print(ANDROID_LOG_INFO, logTag, "Unknown positioningMethod");
225 }
226 }
227
228 env->ReleaseIntArrayElements(jProviders, providers, 0);
229
230 return ret;
231 }
232
233 QGeoPositionInfo positionInfoFromJavaLocation(const jobject &location, bool useAltConverter)
234 {
235 QGeoPositionInfo info;
236
237 QJniObject jniObject(location);
238 if (!jniObject.isValid())
239 return QGeoPositionInfo();
240
241 const jdouble latitude = jniObject.callMethod<jdouble>("getLatitude");
242 const jdouble longitude = jniObject.callMethod<jdouble>("getLongitude");
243
244 QGeoCoordinate coordinate(latitude, longitude);
245
246 // altitude
247 jboolean attributeExists = jniObject.callMethod<jboolean>("hasAltitude");
248 if (attributeExists) {
249 const jdouble value = jniObject.callMethod<jdouble>("getAltitude");
250 if (!qFuzzyIsNull(value))
251 coordinate.setAltitude(value);
252 }
253 // MSL altitude, available in API Level 34+.
254 // In API Level 34 it was available only if we manually added it.
255 // In API Level 35 (and potentially later), it's automatically added
256 // to the location object, so we need to use it *only* when the user
257 // set the relevant plugin parameter.
258 if (useAltConverter && QNativeInterface::QAndroidApplication::sdkVersion() >= 34) {
259 attributeExists = jniObject.callMethod<jboolean>("hasMslAltitude");
260 if (attributeExists) {
261 const jdouble value = jniObject.callMethod<jdouble>("getMslAltitudeMeters");
262 if (!qFuzzyIsNull(value))
263 coordinate.setAltitude(value);
264 }
265 }
266
267 info.setCoordinate(coordinate);
268
269 // time stamp
270 const jlong timestamp = jniObject.callMethod<jlong>("getTime");
271 info.setTimestamp(QDateTime::fromMSecsSinceEpoch(timestamp, QTimeZone::UTC));
272
273 // horizontal accuracy
274 attributeExists = jniObject.callMethod<jboolean>("hasAccuracy");
275 if (attributeExists) {
276 const jfloat accuracy = jniObject.callMethod<jfloat>("getAccuracy");
277 if (!qFuzzyIsNull(accuracy))
278 info.setAttribute(QGeoPositionInfo::HorizontalAccuracy, qreal(accuracy));
279 }
280
281 // vertical accuracy (available in API Level 26+)
282 attributeExists = jniObject.callMethod<jboolean>("hasVerticalAccuracy");
283 if (attributeExists) {
284 const jfloat accuracy = jniObject.callMethod<jfloat>("getVerticalAccuracyMeters");
285 if (!qFuzzyIsNull(accuracy))
286 info.setAttribute(QGeoPositionInfo::VerticalAccuracy, qreal(accuracy));
287 }
288
289 // ground speed
290 attributeExists = jniObject.callMethod<jboolean>("hasSpeed");
291 if (attributeExists) {
292 const jfloat speed = jniObject.callMethod<jfloat>("getSpeed");
293 if (!qFuzzyIsNull(speed))
294 info.setAttribute(QGeoPositionInfo::GroundSpeed, qreal(speed));
295 }
296
297 // bearing
298 attributeExists = jniObject.callMethod<jboolean>("hasBearing");
299 if (attributeExists) {
300 const jfloat bearing = jniObject.callMethod<jfloat>("getBearing");
301 if (!qFuzzyIsNull(bearing))
302 info.setAttribute(QGeoPositionInfo::Direction, qreal(bearing));
303
304 // bearingAccuracy is available in API Level 26+
305 const jfloat bearingAccuracy =
306 jniObject.callMethod<jfloat>("getBearingAccuracyDegrees");
307 if (!qFuzzyIsNull(bearingAccuracy))
308 info.setAttribute(QGeoPositionInfo::DirectionAccuracy, qreal(bearingAccuracy));
309 }
310
311 return info;
312 }
313
314 using UniqueId = std::pair<int, int>;
315 static UniqueId getUid(const QGeoSatelliteInfo &info)
316 {
317 return std::make_pair(static_cast<int>(info.satelliteSystem()),
318 info.satelliteIdentifier());
319 }
320
322 QList<QGeoSatelliteInfo>* usedInFix)
323 {
324 QJniObject jniStatus(gnssStatus);
325 QList<QGeoSatelliteInfo> sats;
326 QSet<UniqueId> uids;
327
328 const int satellitesCount = jniStatus.callMethod<jint>("getSatelliteCount");
329 for (int i = 0; i < satellitesCount; ++i) {
330 QGeoSatelliteInfo info;
331
332 // signal strength - this is actually a carrier-to-noise density,
333 // but the values are very close to what was previously returned by
334 // getSnr() method of the GpsSatellite API.
335 const jfloat cn0 = jniStatus.callMethod<jfloat>("getCn0DbHz", i);
336 info.setSignalStrength(static_cast<int>(cn0));
337
338 // satellite system
339 const jint constellationType =
340 jniStatus.callMethod<jint>("getConstellationType", i);
341 info.setSatelliteSystem(ConstellationMapper::toSatelliteSystem(constellationType));
342
343 // satellite identifier
344 const jint svId = jniStatus.callMethod<jint>("getSvid", i);
345 info.setSatelliteIdentifier(svId);
346
347 // azimuth
348 const jfloat azimuth = jniStatus.callMethod<jfloat>("getAzimuthDegrees", i);
349 info.setAttribute(QGeoSatelliteInfo::Azimuth, static_cast<qreal>(azimuth));
350
351 // elevation
352 const jfloat elevation = jniStatus.callMethod<jfloat>("getElevationDegrees", i);
353 info.setAttribute(QGeoSatelliteInfo::Elevation, static_cast<qreal>(elevation));
354
355 // Used in fix - true if this satellite is actually used in
356 // determining the position.
357 const jboolean inFix = jniStatus.callMethod<jboolean>("usedInFix", i);
358
359 const UniqueId id = getUid(info);
360 if (uids.contains(id))
361 continue;
362
363 sats.append(info);
364 uids.insert(id);
365
366 if (inFix)
367 usedInFix->append(info);
368 }
369
370 return sats;
371 }
372
373 QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly,
374 bool useAltitudeConverter)
375 {
376 QJniEnvironment env;
377 if (!env.jniEnv())
378 return QGeoPositionInfo();
379
380 const auto accuracy = fromSatellitePositioningMethodsOnly
383
384 if (!hasPositioningPermissions(accuracy))
385 return {};
386
387 QJniObject locationObj = QJniObject::callStaticMethod<jobject>(
388 positioningClass(), lastKnownPositionMethodId, fromSatellitePositioningMethodsOnly,
389 useAltitudeConverter);
390 jobject location = locationObj.object();
391 if (location == nullptr)
392 return QGeoPositionInfo();
393
394 const QGeoPositionInfo info = positionInfoFromJavaLocation(location, useAltitudeConverter);
395
396 return info;
397 }
398
399 inline int positioningMethodToInt(QGeoPositionInfoSource::PositioningMethods m)
400 {
401 int providerSelection = 0;
402 if (m & QGeoPositionInfoSource::SatellitePositioningMethods)
403 providerSelection |= 1;
404 if (m & QGeoPositionInfoSource::NonSatellitePositioningMethods)
405 providerSelection |= 2;
406
407 return providerSelection;
408 }
409
410 static AccuracyTypes
411 accuracyFromPositioningMethods(QGeoPositionInfoSource::PositioningMethods m)
412 {
413 AccuracyTypes types = AccuracyType::None;
414 if (m & QGeoPositionInfoSource::NonSatellitePositioningMethods)
416 if (m & QGeoPositionInfoSource::SatellitePositioningMethods)
417 types |= AccuracyType::Precise;
418 return types;
419 }
420
422 {
423 QJniEnvironment env;
424 if (!env.jniEnv())
425 return QGeoPositionInfoSource::UnknownSourceError;
426
427 QGeoPositionInfoSourceAndroid *source = AndroidPositioning::idToPosSource()->value(androidClassKey);
428
429 if (source) {
430 const auto preferredMethods = source->preferredPositioningMethods();
431 const auto accuracy = accuracyFromPositioningMethods(preferredMethods);
432 if (!hasPositioningPermissions(accuracy))
433 return QGeoPositionInfoSource::AccessError;
434
435 int errorCode = QJniObject::callStaticMethod<jint>(
436 positioningClass(), startUpdatesMethodId, androidClassKey,
437 positioningMethodToInt(preferredMethods),
438 source->updateInterval(), source->useAltitudeConverter());
439 switch (errorCode) {
440 case 0:
441 case 1:
442 case 2:
443 case 3:
444 return static_cast<QGeoPositionInfoSource::Error>(errorCode);
445 default:
446 break;
447 }
448 }
449
450 return QGeoPositionInfoSource::UnknownSourceError;
451 }
452
453 //used for stopping regular and single updates
454 void stopUpdates(int androidClassKey)
455 {
456 QJniObject::callStaticMethod<void>(positioningClass(), stopUpdatesMethodId,
457 androidClassKey);
458 }
459
460 QGeoPositionInfoSource::Error requestUpdate(int androidClassKey, int timeout)
461 {
462 QJniEnvironment env;
463 if (!env.jniEnv())
464 return QGeoPositionInfoSource::UnknownSourceError;
465
466 QGeoPositionInfoSourceAndroid *source = AndroidPositioning::idToPosSource()->value(androidClassKey);
467
468 if (source) {
469 const auto preferredMethods = source->preferredPositioningMethods();
470 const auto accuracy = accuracyFromPositioningMethods(preferredMethods);
471 if (!hasPositioningPermissions(accuracy))
472 return QGeoPositionInfoSource::AccessError;
473
474 int errorCode = QJniObject::callStaticMethod<jint>(
475 positioningClass(), requestUpdateMethodId, androidClassKey,
476 positioningMethodToInt(preferredMethods),
477 timeout, source->useAltitudeConverter());
478 switch (errorCode) {
479 case 0:
480 case 1:
481 case 2:
482 case 3:
483 return static_cast<QGeoPositionInfoSource::Error>(errorCode);
484 default:
485 break;
486 }
487 }
488 return QGeoPositionInfoSource::UnknownSourceError;
489 }
490
491 QGeoSatelliteInfoSource::Error startSatelliteUpdates(int androidClassKey, bool isSingleRequest, int requestTimeout)
492 {
493 QJniEnvironment env;
494 if (!env.jniEnv())
495 return QGeoSatelliteInfoSource::UnknownSourceError;
496
497 QGeoSatelliteInfoSourceAndroid *source = AndroidPositioning::idToSatSource()->value(androidClassKey);
498
499 if (source) {
500 // Satellite Info request does not make sense with Approximate
501 // location permissions, so always check for Precise
502 if (!hasPositioningPermissions(AccuracyType::Precise))
503 return QGeoSatelliteInfoSource::AccessError;
504
505 int interval = source->updateInterval();
506 if (isSingleRequest)
507 interval = requestTimeout;
508 int errorCode = QJniObject::callStaticMethod<jint>(positioningClass(),
509 startSatelliteUpdatesMethodId,
510 androidClassKey, interval,
511 isSingleRequest);
512 switch (errorCode) {
513 case -1:
514 case 0:
515 case 1:
516 case 2:
517 return static_cast<QGeoSatelliteInfoSource::Error>(errorCode);
518 default:
519 qCWarning(lcPositioning)
520 << "startSatelliteUpdates: Unknown error code" << errorCode;
521 break;
522 }
523 }
524 return QGeoSatelliteInfoSource::UnknownSourceError;
525 }
526
527
528 bool hasPositioningPermissions(AccuracyTypes accuracy)
529 {
530 QLocationPermission permission;
531
532 // The needed permission depends on whether we run as a service or as an activity
533 if (!QNativeInterface::QAndroidApplication::isActivityContext())
534 permission.setAvailability(QLocationPermission::Always); // background location
535
536 bool permitted = false;
537 if (accuracy & AccuracyType::Precise) {
538 permission.setAccuracy(QLocationPermission::Precise);
539 permitted = qApp->checkPermission(permission) == Qt::PermissionStatus::Granted;
540 }
541 if (accuracy & AccuracyType::Approximate) {
542 permission.setAccuracy(QLocationPermission::Approximate);
543 permitted |= qApp->checkPermission(permission) == Qt::PermissionStatus::Granted;
544 }
545
546 if (!permitted)
547 qCWarning(lcPositioning) << "Position data not available due to missing permission";
548
549 return permitted;
550 }
551}
552
553static void positionUpdated(JNIEnv *env, jobject thiz, QtJniTypes::Location location,
554 jint androidClassKey, jboolean isSingleUpdate)
555{
556 Q_UNUSED(env);
557 Q_UNUSED(thiz);
558
559 QGeoPositionInfoSourceAndroid *source = AndroidPositioning::idToPosSource()->value(androidClassKey);
560 if (!source) {
561 qCWarning(lcPositioning) << "positionUpdated: source == 0";
562 return;
563 }
564
565 const bool useAltitudeConverter = source->useAltitudeConverter();
566 QGeoPositionInfo info =
567 AndroidPositioning::positionInfoFromJavaLocation(location.object(), useAltitudeConverter);
568
569 //we need to invoke indirectly as the Looper thread is likely to be not the same thread
570 if (!isSingleUpdate)
571 QMetaObject::invokeMethod(source, "processPositionUpdate", Qt::AutoConnection,
572 Q_ARG(QGeoPositionInfo, info));
573 else
574 QMetaObject::invokeMethod(source, "processSinglePositionUpdate", Qt::AutoConnection,
575 Q_ARG(QGeoPositionInfo, info));
576}
577Q_DECLARE_JNI_NATIVE_METHOD(positionUpdated)
578
579static void locationProvidersDisabled(JNIEnv *env, jobject thiz, jint androidClassKey)
580{
581 Q_UNUSED(env);
582 Q_UNUSED(thiz);
583 QObject *source = AndroidPositioning::idToPosSource()->value(androidClassKey);
584 if (!source)
585 source = AndroidPositioning::idToSatSource()->value(androidClassKey);
586 if (!source) {
587 qCWarning(lcPositioning) << "locationProvidersDisabled: source == 0";
588 return;
589 }
590
591 QMetaObject::invokeMethod(source, "locationProviderDisabled", Qt::AutoConnection);
592}
593Q_DECLARE_JNI_NATIVE_METHOD(locationProvidersDisabled)
594
595static void locationProvidersChanged(JNIEnv *env, jobject thiz, jint androidClassKey)
596{
597 Q_UNUSED(env);
598 Q_UNUSED(thiz);
599 QObject *source = AndroidPositioning::idToPosSource()->value(androidClassKey);
600 if (!source) {
601 qCWarning(lcPositioning) << "locationProvidersChanged: source == 0";
602 return;
603 }
604
605 QMetaObject::invokeMethod(source, "locationProvidersChanged", Qt::AutoConnection);
606}
607Q_DECLARE_JNI_NATIVE_METHOD(locationProvidersChanged)
608
609static void notifySatelliteInfoUpdated(const QList<QGeoSatelliteInfo> &inView,
610 const QList<QGeoSatelliteInfo> &inUse,
611 jint androidClassKey, jboolean isSingleUpdate)
612{
613 QGeoSatelliteInfoSourceAndroid *source = AndroidPositioning::idToSatSource()->value(androidClassKey);
614 if (!source) {
615 qCWarning(lcPositioning) << "notifySatelliteInfoUpdated: source == 0";
616 return;
617 }
618
619 QMetaObject::invokeMethod(source, "processSatelliteUpdate", Qt::AutoConnection,
620 Q_ARG(QList<QGeoSatelliteInfo>, inView),
621 Q_ARG(QList<QGeoSatelliteInfo>, inUse),
622 Q_ARG(bool, isSingleUpdate));
623}
624
625static void satelliteGnssUpdated(JNIEnv *env, jobject thiz, QtJniTypes::GnssStatus gnssStatus,
626 jint androidClassKey, jboolean isSingleUpdate)
627{
628 Q_UNUSED(env);
629 Q_UNUSED(thiz);
630
631 QList<QGeoSatelliteInfo> inUse;
632 QList<QGeoSatelliteInfo> sats =
633 AndroidPositioning::satelliteInfoFromJavaGnssStatus(gnssStatus.object(), &inUse);
634
635 notifySatelliteInfoUpdated(sats, inUse, androidClassKey, isSingleUpdate);
636}
637Q_DECLARE_JNI_NATIVE_METHOD(satelliteGnssUpdated)
638
639#define GET_AND_CHECK_STATIC_METHOD(VAR, METHOD_NAME, ...)
640 VAR = env.findStaticMethod<__VA_ARGS__>(positioningClass(), METHOD_NAME);
641 if (!VAR) {
642 __android_log_print(ANDROID_LOG_FATAL, logTag, methodErrorMsg, METHOD_NAME,
643 QtJniTypes::methodSignature<__VA_ARGS__>().data());
644 return false;
645 }
646
647static bool registerNatives()
648{
649 QJniEnvironment env;
650 if (!env.jniEnv()) {
651 __android_log_print(ANDROID_LOG_FATAL, logTag, "Failed to create environment");
652 return false;
653 }
654
655 if (!positioningClass.init()) {
656 __android_log_print(ANDROID_LOG_FATAL, logTag, "Failed to create global class ref");
657 return false;
658 }
659
660 if (!env.registerNativeMethods(positioningClass(), {
661 Q_JNI_NATIVE_METHOD(positionUpdated),
662 Q_JNI_NATIVE_METHOD(locationProvidersDisabled),
663 Q_JNI_NATIVE_METHOD(locationProvidersChanged),
664 Q_JNI_NATIVE_METHOD(satelliteGnssUpdated)
665 })) {
666 __android_log_print(ANDROID_LOG_FATAL, logTag, "Failed to register native methods");
667 return false;
668 }
669
670 GET_AND_CHECK_STATIC_METHOD(providerListMethodId, "providerList", jintArray);
671 GET_AND_CHECK_STATIC_METHOD(lastKnownPositionMethodId, "lastKnownPosition",
672 QtJniTypes::Location, bool, bool);
673 GET_AND_CHECK_STATIC_METHOD(startUpdatesMethodId, "startUpdates", jint, jint, jint, jint,
674 bool);
675 GET_AND_CHECK_STATIC_METHOD(stopUpdatesMethodId, "stopUpdates", void, jint);
676 GET_AND_CHECK_STATIC_METHOD(requestUpdateMethodId, "requestUpdate", jint, jint, jint, jint,
677 bool);
678 GET_AND_CHECK_STATIC_METHOD(startSatelliteUpdatesMethodId, "startSatelliteUpdates",
679 jint, jint, jint, bool);
680
681 return true;
682}
683
684extern "C" Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM * /*vm*/, void * /*reserved*/)
685{
686 static bool initialized = false;
687 if (initialized)
688 return JNI_VERSION_1_6;
689 initialized = true;
690
691 __android_log_print(ANDROID_LOG_INFO, logTag, "Positioning start");
692
693 const auto context = QNativeInterface::QAndroidApplication::context();
694 QtJniTypes::QtPositioning::callStaticMethod<void>("setContext", context);
695
696 if (!registerNatives()) {
697 __android_log_print(ANDROID_LOG_FATAL, logTag, "registerNatives() failed");
698 return -1;
699 }
700
701 if (!ConstellationMapper::init()) {
702 __android_log_print(ANDROID_LOG_ERROR, logTag,
703 "Failed to extract constellation type constants. "
704 "Satellite system will be undefined!");
705 }
706
707 return JNI_VERSION_1_6;
708}
#define GET_AND_CHECK_STATIC_METHOD(VAR, CLASS, METHOD_NAME, METHOD_SIGNATURE)
GlobalClassRefWrapper()=default
static jmethodID startUpdatesMethodId
static void positionUpdated(JNIEnv *env, jobject thiz, QtJniTypes::Location location, jint androidClassKey, jboolean isSingleUpdate)
static jmethodID requestUpdateMethodId
static void notifySatelliteInfoUpdated(const QList< QGeoSatelliteInfo > &inView, const QList< QGeoSatelliteInfo > &inUse, jint androidClassKey, jboolean isSingleUpdate)
static jmethodID providerListMethodId
static jmethodID lastKnownPositionMethodId
static GlobalClassRefWrapper< QtJniTypes::QtPositioning > positioningClass
static const char logTag[]
static void satelliteGnssUpdated(JNIEnv *env, jobject thiz, QtJniTypes::GnssStatus gnssStatus, jint androidClassKey, jboolean isSingleUpdate)
static void locationProvidersChanged(JNIEnv *env, jobject thiz, jint androidClassKey)
static jmethodID stopUpdatesMethodId
static const char methodErrorMsg[]
static void locationProvidersDisabled(JNIEnv *env, jobject thiz, jint androidClassKey)
static bool registerNatives()
static jmethodID startSatelliteUpdatesMethodId
static UniqueId getUid(const QGeoSatelliteInfo &info)
QList< QGeoSatelliteInfo > satelliteInfoFromJavaGnssStatus(jobject gnssStatus, QList< QGeoSatelliteInfo > *usedInFix)
QMap< int, QGeoSatelliteInfoSourceAndroid * > SatelliteSourceMap
int positioningMethodToInt(QGeoPositionInfoSource::PositioningMethods m)
QGeoSatelliteInfoSource::Error startSatelliteUpdates(int androidClassKey, bool isSingleRequest, int requestTimeout)
QGeoPositionInfoSource::Error requestUpdate(int androidClassKey, int timeout)
QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly, bool useAltitudeConverter)
QGeoPositionInfoSource::Error startUpdates(int androidClassKey)
static AccuracyTypes accuracyFromPositioningMethods(QGeoPositionInfoSource::PositioningMethods m)
QMap< int, QGeoPositionInfoSourceAndroid * > PositionSourceMap
QGeoPositionInfoSource::PositioningMethods availableProviders()
QGeoPositionInfo positionInfoFromJavaLocation(const jobject &location, bool useAltConverter)
void stopUpdates(int androidClassKey)
bool hasPositioningPermissions(AccuracyTypes accuracy)
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")
Q_DECLARE_JNI_CLASS(MotionEvent, "android/view/MotionEvent")