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 = nullptr;
88 if (QNativeInterface::QAndroidApplication::sdkVersion() > 23) {
89 m_gnssStatusObject = QJniEnvironment().findClass<QtJniTypes::GnssStatus>();
90 if (!m_gnssStatusObject)
91 return false;
92 }
93 // no need to query it for API level <= 23
94 return true;
95 }
96
97 static QGeoSatelliteInfo::SatelliteSystem toSatelliteSystem(int constellationType)
98 {
99 if (!m_gnssStatusObject)
100 return QGeoSatelliteInfo::Undefined;
101
102 static const int gps =
103 QJniObject::getStaticField<jint>(m_gnssStatusObject, "CONSTELLATION_GPS");
104 static const int glonass =
105 QJniObject::getStaticField<jint>(m_gnssStatusObject, "CONSTELLATION_GLONASS");
106 static const int galileo =
107 QJniObject::getStaticField<jint>(m_gnssStatusObject, "CONSTELLATION_GALILEO");
108 static const int beidou =
109 QJniObject::getStaticField<jint>(m_gnssStatusObject, "CONSTELLATION_BEIDOU");
110 static const int qzss =
111 QJniObject::getStaticField<jint>(m_gnssStatusObject, "CONSTELLATION_QZSS");
112
113 if (constellationType == gps) {
114 return QGeoSatelliteInfo::GPS;
115 } else if (constellationType == glonass) {
116 return QGeoSatelliteInfo::GLONASS;
117 } else if (constellationType == galileo) {
118 return QGeoSatelliteInfo::GALILEO;
119 } else if (constellationType == beidou) {
120 return QGeoSatelliteInfo::BEIDOU;
121 } else if (constellationType == qzss){
122 return QGeoSatelliteInfo::QZSS;
123 } else {
124 qCWarning(lcPositioning) << "Unknown satellite system" << constellationType;
125 return QGeoSatelliteInfo::Undefined;
126 }
127 }
128
129private:
130 static jclass m_gnssStatusObject;
131};
132
133jclass ConstellationMapper::m_gnssStatusObject = nullptr;
134
135} // anonymous namespace
136
140
142
144
146 {
147 int key = -1;
148 if (obj->inherits("QGeoPositionInfoSource")) {
150 Q_ASSERT(src);
151 do {
153 } while (idToPosSource()->contains(key));
154
156 } else if (obj->inherits("QGeoSatelliteInfoSource")) {
158 Q_ASSERT(src);
159 do {
161 } while (idToSatSource()->contains(key));
162
164 }
165
166 return key;
167 }
168
177
184
185
187 {
188 QGeoPositionInfoSource::PositioningMethods ret = QGeoPositionInfoSource::NoPositioningMethods;
189 QJniEnvironment env;
190 if (!env.jniEnv())
191 return ret;
192 QJniObject jniProvidersObj =
193 QJniObject::callStaticMethod<jobject>(positioningClass(), providerListMethodId);
194 jintArray jProviders = jniProvidersObj.object<jintArray>();
195 if (!jProviders) {
196 // Work-around for QTBUG-116645
197 __android_log_print(ANDROID_LOG_INFO, logTag, "Got null providers array!");
198 return ret;
199 }
200 jint *providers = env->GetIntArrayElements(jProviders, nullptr);
201 const int size = env->GetArrayLength(jProviders);
202 for (int i = 0; i < size; i++) {
203 switch (providers[i]) {
204 case PROVIDER_GPS:
205 ret |= QGeoPositionInfoSource::SatellitePositioningMethods;
206 break;
207 case PROVIDER_NETWORK:
208 ret |= QGeoPositionInfoSource::NonSatellitePositioningMethods;
209 break;
210 case PROVIDER_PASSIVE:
211 //we ignore as Qt doesn't have interface for it right now
212 break;
213 default:
214 __android_log_print(ANDROID_LOG_INFO, logTag, "Unknown positioningMethod");
215 }
216 }
217
218 env->ReleaseIntArrayElements(jProviders, providers, 0);
219
220 return ret;
221 }
222
223 QGeoPositionInfo positionInfoFromJavaLocation(const jobject &location, bool useAltConverter)
224 {
225 QGeoPositionInfo info;
226
227 QJniObject jniObject(location);
228 if (!jniObject.isValid())
229 return QGeoPositionInfo();
230
231 const jdouble latitude = jniObject.callMethod<jdouble>("getLatitude");
232 const jdouble longitude = jniObject.callMethod<jdouble>("getLongitude");
233
234 QGeoCoordinate coordinate(latitude, longitude);
235
236 // altitude
237 jboolean attributeExists = jniObject.callMethod<jboolean>("hasAltitude");
238 if (attributeExists) {
239 const jdouble value = jniObject.callMethod<jdouble>("getAltitude");
240 if (!qFuzzyIsNull(value))
241 coordinate.setAltitude(value);
242 }
243 // MSL altitude, available in API Level 34+.
244 // In API Level 34 it was available only if we manually added it.
245 // In API Level 35 (and potentially later), it's automatically added
246 // to the location object, so we need to use it *only* when the user
247 // set the relevant plugin parameter.
248 if (useAltConverter && QNativeInterface::QAndroidApplication::sdkVersion() >= 34) {
249 attributeExists = jniObject.callMethod<jboolean>("hasMslAltitude");
250 if (attributeExists) {
251 const jdouble value = jniObject.callMethod<jdouble>("getMslAltitudeMeters");
252 if (!qFuzzyIsNull(value))
253 coordinate.setAltitude(value);
254 }
255 }
256
257 info.setCoordinate(coordinate);
258
259 // time stamp
260 const jlong timestamp = jniObject.callMethod<jlong>("getTime");
261 info.setTimestamp(QDateTime::fromMSecsSinceEpoch(timestamp, QTimeZone::UTC));
262
263 // horizontal accuracy
264 attributeExists = jniObject.callMethod<jboolean>("hasAccuracy");
265 if (attributeExists) {
266 const jfloat accuracy = jniObject.callMethod<jfloat>("getAccuracy");
267 if (!qFuzzyIsNull(accuracy))
268 info.setAttribute(QGeoPositionInfo::HorizontalAccuracy, qreal(accuracy));
269 }
270
271 // vertical accuracy (available in API Level 26+)
272 if (QNativeInterface::QAndroidApplication::sdkVersion() > 25) {
273 attributeExists = jniObject.callMethod<jboolean>("hasVerticalAccuracy");
274 if (attributeExists) {
275 const jfloat accuracy = jniObject.callMethod<jfloat>("getVerticalAccuracyMeters");
276 if (!qFuzzyIsNull(accuracy))
277 info.setAttribute(QGeoPositionInfo::VerticalAccuracy, qreal(accuracy));
278 }
279 }
280
281 // ground speed
282 attributeExists = jniObject.callMethod<jboolean>("hasSpeed");
283 if (attributeExists) {
284 const jfloat speed = jniObject.callMethod<jfloat>("getSpeed");
285 if (!qFuzzyIsNull(speed))
286 info.setAttribute(QGeoPositionInfo::GroundSpeed, qreal(speed));
287 }
288
289 // bearing
290 attributeExists = jniObject.callMethod<jboolean>("hasBearing");
291 if (attributeExists) {
292 const jfloat bearing = jniObject.callMethod<jfloat>("getBearing");
293 if (!qFuzzyIsNull(bearing))
294 info.setAttribute(QGeoPositionInfo::Direction, qreal(bearing));
295
296 // bearingAccuracy is available in API Level 26+
297 if (QNativeInterface::QAndroidApplication::sdkVersion() > 25) {
298 const jfloat bearingAccuracy =
299 jniObject.callMethod<jfloat>("getBearingAccuracyDegrees");
300 if (!qFuzzyIsNull(bearingAccuracy))
301 info.setAttribute(QGeoPositionInfo::DirectionAccuracy, qreal(bearingAccuracy));
302 }
303 }
304
305 return info;
306 }
307
308 using UniqueId = std::pair<int, int>;
309 static UniqueId getUid(const QGeoSatelliteInfo &info)
310 {
311 return std::make_pair(static_cast<int>(info.satelliteSystem()),
312 info.satelliteIdentifier());
313 }
314
316 jobjectArray satellites,
317 QList<QGeoSatelliteInfo>* usedInFix)
318 {
319 QSet<UniqueId> uids;
320 QList<QGeoSatelliteInfo> sats;
321 jsize length = jniEnv->GetArrayLength(satellites);
322 for (int i = 0; i<length; i++) {
323 jobject element = jniEnv->GetObjectArrayElement(satellites, i);
324 if (QJniEnvironment::checkAndClearExceptions(jniEnv)) {
325 qCWarning(lcPositioning) << "Cannot process all satellite data due to exception.";
326 break;
327 }
328
329 QJniObject jniObj = QJniObject::fromLocalRef(element);
330 if (!jniObj.isValid())
331 continue;
332
333 QGeoSatelliteInfo info;
334
335 // signal strength
336 const jfloat snr = jniObj.callMethod<jfloat>("getSnr");
337 info.setSignalStrength(int(snr));
338
339 // ignore any satellite with no signal whatsoever
340 if (qFuzzyIsNull(snr))
341 continue;
342
343 // prn
344 const jint prn = jniObj.callMethod<jint>("getPrn");
345 info.setSatelliteIdentifier(prn);
346
347 if (prn >= 1 && prn <= 32)
348 info.setSatelliteSystem(QGeoSatelliteInfo::GPS);
349 else if (prn >= 65 && prn <= 96)
350 info.setSatelliteSystem(QGeoSatelliteInfo::GLONASS);
351 else if (prn >= 193 && prn <= 200)
352 info.setSatelliteSystem(QGeoSatelliteInfo::QZSS);
353 else if ((prn >= 201 && prn <= 235) || (prn >= 401 && prn <= 437))
354 info.setSatelliteSystem(QGeoSatelliteInfo::BEIDOU);
355 else if (prn >= 301 && prn <= 336)
356 info.setSatelliteSystem(QGeoSatelliteInfo::GALILEO);
357
358 // azimuth
359 const jfloat azimuth = jniObj.callMethod<jfloat>("getAzimuth");
360 info.setAttribute(QGeoSatelliteInfo::Azimuth, qreal(azimuth));
361
362 // elevation
363 const jfloat elevation = jniObj.callMethod<jfloat>("getElevation");
364 info.setAttribute(QGeoSatelliteInfo::Elevation, qreal(elevation));
365
366 // Used in fix - true if this satellite is actually used in
367 // determining the position.
368 const jboolean inFix = jniObj.callMethod<jboolean>("usedInFix");
369
370 const UniqueId id = getUid(info);
371 if (uids.contains(id))
372 continue;
373
374 sats.append(info);
375 uids.insert(id);
376
377 if (inFix)
378 usedInFix->append(info);
379 }
380
381 return sats;
382 }
383
385 QList<QGeoSatelliteInfo>* usedInFix)
386 {
387 QJniObject jniStatus(gnssStatus);
388 QList<QGeoSatelliteInfo> sats;
389 QSet<UniqueId> uids;
390
391 const int satellitesCount = jniStatus.callMethod<jint>("getSatelliteCount");
392 for (int i = 0; i < satellitesCount; ++i) {
393 QGeoSatelliteInfo info;
394
395 // signal strength - this is actually a carrier-to-noise density,
396 // but the values are very close to what was previously returned by
397 // getSnr() method of the GpsSatellite API.
398 const jfloat cn0 = jniStatus.callMethod<jfloat>("getCn0DbHz", i);
399 info.setSignalStrength(static_cast<int>(cn0));
400
401 // satellite system
402 const jint constellationType =
403 jniStatus.callMethod<jint>("getConstellationType", i);
404 info.setSatelliteSystem(ConstellationMapper::toSatelliteSystem(constellationType));
405
406 // satellite identifier
407 const jint svId = jniStatus.callMethod<jint>("getSvid", i);
408 info.setSatelliteIdentifier(svId);
409
410 // azimuth
411 const jfloat azimuth = jniStatus.callMethod<jfloat>("getAzimuthDegrees", i);
412 info.setAttribute(QGeoSatelliteInfo::Azimuth, static_cast<qreal>(azimuth));
413
414 // elevation
415 const jfloat elevation = jniStatus.callMethod<jfloat>("getElevationDegrees", i);
416 info.setAttribute(QGeoSatelliteInfo::Elevation, static_cast<qreal>(elevation));
417
418 // Used in fix - true if this satellite is actually used in
419 // determining the position.
420 const jboolean inFix = jniStatus.callMethod<jboolean>("usedInFix", i);
421
422 const UniqueId id = getUid(info);
423 if (uids.contains(id))
424 continue;
425
426 sats.append(info);
427 uids.insert(id);
428
429 if (inFix)
430 usedInFix->append(info);
431 }
432
433 return sats;
434 }
435
436 QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly,
437 bool useAltitudeConverter)
438 {
439 QJniEnvironment env;
440 if (!env.jniEnv())
441 return QGeoPositionInfo();
442
443 const auto accuracy = fromSatellitePositioningMethodsOnly
446
447 if (!hasPositioningPermissions(accuracy))
448 return {};
449
450 QJniObject locationObj = QJniObject::callStaticMethod<jobject>(
451 positioningClass(), lastKnownPositionMethodId, fromSatellitePositioningMethodsOnly,
452 useAltitudeConverter);
453 jobject location = locationObj.object();
454 if (location == nullptr)
455 return QGeoPositionInfo();
456
457 const QGeoPositionInfo info = positionInfoFromJavaLocation(location, useAltitudeConverter);
458
459 return info;
460 }
461
462 inline int positioningMethodToInt(QGeoPositionInfoSource::PositioningMethods m)
463 {
464 int providerSelection = 0;
465 if (m & QGeoPositionInfoSource::SatellitePositioningMethods)
466 providerSelection |= 1;
467 if (m & QGeoPositionInfoSource::NonSatellitePositioningMethods)
468 providerSelection |= 2;
469
470 return providerSelection;
471 }
472
473 static AccuracyTypes
474 accuracyFromPositioningMethods(QGeoPositionInfoSource::PositioningMethods m)
475 {
476 AccuracyTypes types = AccuracyType::None;
477 if (m & QGeoPositionInfoSource::NonSatellitePositioningMethods)
479 if (m & QGeoPositionInfoSource::SatellitePositioningMethods)
480 types |= AccuracyType::Precise;
481 return types;
482 }
483
485 {
486 QJniEnvironment env;
487 if (!env.jniEnv())
488 return QGeoPositionInfoSource::UnknownSourceError;
489
490 QGeoPositionInfoSourceAndroid *source = AndroidPositioning::idToPosSource()->value(androidClassKey);
491
492 if (source) {
493 const auto preferredMethods = source->preferredPositioningMethods();
494 const auto accuracy = accuracyFromPositioningMethods(preferredMethods);
495 if (!hasPositioningPermissions(accuracy))
496 return QGeoPositionInfoSource::AccessError;
497
498 int errorCode = QJniObject::callStaticMethod<jint>(
499 positioningClass(), startUpdatesMethodId, androidClassKey,
500 positioningMethodToInt(preferredMethods),
501 source->updateInterval(), source->useAltitudeConverter());
502 switch (errorCode) {
503 case 0:
504 case 1:
505 case 2:
506 case 3:
507 return static_cast<QGeoPositionInfoSource::Error>(errorCode);
508 default:
509 break;
510 }
511 }
512
513 return QGeoPositionInfoSource::UnknownSourceError;
514 }
515
516 //used for stopping regular and single updates
517 void stopUpdates(int androidClassKey)
518 {
519 QJniObject::callStaticMethod<void>(positioningClass(), stopUpdatesMethodId,
520 androidClassKey);
521 }
522
523 QGeoPositionInfoSource::Error requestUpdate(int androidClassKey, int timeout)
524 {
525 QJniEnvironment env;
526 if (!env.jniEnv())
527 return QGeoPositionInfoSource::UnknownSourceError;
528
529 QGeoPositionInfoSourceAndroid *source = AndroidPositioning::idToPosSource()->value(androidClassKey);
530
531 if (source) {
532 const auto preferredMethods = source->preferredPositioningMethods();
533 const auto accuracy = accuracyFromPositioningMethods(preferredMethods);
534 if (!hasPositioningPermissions(accuracy))
535 return QGeoPositionInfoSource::AccessError;
536
537 int errorCode = QJniObject::callStaticMethod<jint>(
538 positioningClass(), requestUpdateMethodId, androidClassKey,
539 positioningMethodToInt(preferredMethods),
540 timeout, source->useAltitudeConverter());
541 switch (errorCode) {
542 case 0:
543 case 1:
544 case 2:
545 case 3:
546 return static_cast<QGeoPositionInfoSource::Error>(errorCode);
547 default:
548 break;
549 }
550 }
551 return QGeoPositionInfoSource::UnknownSourceError;
552 }
553
554 QGeoSatelliteInfoSource::Error startSatelliteUpdates(int androidClassKey, bool isSingleRequest, int requestTimeout)
555 {
556 QJniEnvironment env;
557 if (!env.jniEnv())
558 return QGeoSatelliteInfoSource::UnknownSourceError;
559
560 QGeoSatelliteInfoSourceAndroid *source = AndroidPositioning::idToSatSource()->value(androidClassKey);
561
562 if (source) {
563 // Satellite Info request does not make sense with Approximate
564 // location permissions, so always check for Precise
565 if (!hasPositioningPermissions(AccuracyType::Precise))
566 return QGeoSatelliteInfoSource::AccessError;
567
568 int interval = source->updateInterval();
569 if (isSingleRequest)
570 interval = requestTimeout;
571 int errorCode = QJniObject::callStaticMethod<jint>(positioningClass(),
572 startSatelliteUpdatesMethodId,
573 androidClassKey, interval,
574 isSingleRequest);
575 switch (errorCode) {
576 case -1:
577 case 0:
578 case 1:
579 case 2:
580 return static_cast<QGeoSatelliteInfoSource::Error>(errorCode);
581 default:
582 qCWarning(lcPositioning)
583 << "startSatelliteUpdates: Unknown error code" << errorCode;
584 break;
585 }
586 }
587 return QGeoSatelliteInfoSource::UnknownSourceError;
588 }
589
590
591 bool hasPositioningPermissions(AccuracyTypes accuracy)
592 {
593 QLocationPermission permission;
594
595 // The needed permission depends on whether we run as a service or as an activity
596 if (!QNativeInterface::QAndroidApplication::isActivityContext())
597 permission.setAvailability(QLocationPermission::Always); // background location
598
599 bool permitted = false;
600 if (accuracy & AccuracyType::Precise) {
601 permission.setAccuracy(QLocationPermission::Precise);
602 permitted = qApp->checkPermission(permission) == Qt::PermissionStatus::Granted;
603 }
604 if (accuracy & AccuracyType::Approximate) {
605 permission.setAccuracy(QLocationPermission::Approximate);
606 permitted |= qApp->checkPermission(permission) == Qt::PermissionStatus::Granted;
607 }
608
609 if (!permitted)
610 qCWarning(lcPositioning) << "Position data not available due to missing permission";
611
612 return permitted;
613 }
614}
615
616static void positionUpdated(JNIEnv *env, jobject thiz, QtJniTypes::Location location,
617 jint androidClassKey, jboolean isSingleUpdate)
618{
619 Q_UNUSED(env);
620 Q_UNUSED(thiz);
621
622 QGeoPositionInfoSourceAndroid *source = AndroidPositioning::idToPosSource()->value(androidClassKey);
623 if (!source) {
624 qCWarning(lcPositioning) << "positionUpdated: source == 0";
625 return;
626 }
627
628 const bool useAltitudeConverter = source->useAltitudeConverter();
629 QGeoPositionInfo info =
630 AndroidPositioning::positionInfoFromJavaLocation(location.object(), useAltitudeConverter);
631
632 //we need to invoke indirectly as the Looper thread is likely to be not the same thread
633 if (!isSingleUpdate)
634 QMetaObject::invokeMethod(source, "processPositionUpdate", Qt::AutoConnection,
635 Q_ARG(QGeoPositionInfo, info));
636 else
637 QMetaObject::invokeMethod(source, "processSinglePositionUpdate", Qt::AutoConnection,
638 Q_ARG(QGeoPositionInfo, info));
639}
640Q_DECLARE_JNI_NATIVE_METHOD(positionUpdated)
641
642static void locationProvidersDisabled(JNIEnv *env, jobject thiz, jint androidClassKey)
643{
644 Q_UNUSED(env);
645 Q_UNUSED(thiz);
646 QObject *source = AndroidPositioning::idToPosSource()->value(androidClassKey);
647 if (!source)
648 source = AndroidPositioning::idToSatSource()->value(androidClassKey);
649 if (!source) {
650 qCWarning(lcPositioning) << "locationProvidersDisabled: source == 0";
651 return;
652 }
653
654 QMetaObject::invokeMethod(source, "locationProviderDisabled", Qt::AutoConnection);
655}
656Q_DECLARE_JNI_NATIVE_METHOD(locationProvidersDisabled)
657
658static void locationProvidersChanged(JNIEnv *env, jobject thiz, jint androidClassKey)
659{
660 Q_UNUSED(env);
661 Q_UNUSED(thiz);
662 QObject *source = AndroidPositioning::idToPosSource()->value(androidClassKey);
663 if (!source) {
664 qCWarning(lcPositioning) << "locationProvidersChanged: source == 0";
665 return;
666 }
667
668 QMetaObject::invokeMethod(source, "locationProvidersChanged", Qt::AutoConnection);
669}
670Q_DECLARE_JNI_NATIVE_METHOD(locationProvidersChanged)
671
672static void notifySatelliteInfoUpdated(const QList<QGeoSatelliteInfo> &inView,
673 const QList<QGeoSatelliteInfo> &inUse,
674 jint androidClassKey, jboolean isSingleUpdate)
675{
676 QGeoSatelliteInfoSourceAndroid *source = AndroidPositioning::idToSatSource()->value(androidClassKey);
677 if (!source) {
678 qCWarning(lcPositioning) << "notifySatelliteInfoUpdated: source == 0";
679 return;
680 }
681
682 QMetaObject::invokeMethod(source, "processSatelliteUpdate", Qt::AutoConnection,
683 Q_ARG(QList<QGeoSatelliteInfo>, inView),
684 Q_ARG(QList<QGeoSatelliteInfo>, inUse),
685 Q_ARG(bool, isSingleUpdate));
686}
687
688static void satelliteGpsUpdated(JNIEnv *env, jobject thiz,
689 jobjectArray satellites,
690 jint androidClassKey, jboolean isSingleUpdate)
691{
692 Q_UNUSED(thiz);
693 QList<QGeoSatelliteInfo> inUse;
694 QList<QGeoSatelliteInfo> sats =
695 AndroidPositioning::satelliteInfoFromJavaLocation(env, satellites, &inUse);
696
697 notifySatelliteInfoUpdated(sats, inUse, androidClassKey, isSingleUpdate);
698}
699Q_DECLARE_JNI_NATIVE_METHOD(satelliteGpsUpdated)
700
701static void satelliteGnssUpdated(JNIEnv *env, jobject thiz, QtJniTypes::GnssStatus gnssStatus,
702 jint androidClassKey, jboolean isSingleUpdate)
703{
704 Q_UNUSED(env);
705 Q_UNUSED(thiz);
706
707 QList<QGeoSatelliteInfo> inUse;
708 QList<QGeoSatelliteInfo> sats =
709 AndroidPositioning::satelliteInfoFromJavaGnssStatus(gnssStatus.object(), &inUse);
710
711 notifySatelliteInfoUpdated(sats, inUse, androidClassKey, isSingleUpdate);
712}
713Q_DECLARE_JNI_NATIVE_METHOD(satelliteGnssUpdated)
714
715#define GET_AND_CHECK_STATIC_METHOD(VAR, METHOD_NAME, ...)
716 VAR = env.findStaticMethod<__VA_ARGS__>(positioningClass(), METHOD_NAME);
717 if (!VAR) {
718 __android_log_print(ANDROID_LOG_FATAL, logTag, methodErrorMsg, METHOD_NAME,
719 QtJniTypes::methodSignature<__VA_ARGS__>().data());
720 return false;
721 }
722
723static bool registerNatives()
724{
725 QJniEnvironment env;
726 if (!env.jniEnv()) {
727 __android_log_print(ANDROID_LOG_FATAL, logTag, "Failed to create environment");
728 return false;
729 }
730
731 if (!positioningClass.init()) {
732 __android_log_print(ANDROID_LOG_FATAL, logTag, "Failed to create global class ref");
733 return false;
734 }
735
736 if (!env.registerNativeMethods(positioningClass(), {
737 Q_JNI_NATIVE_METHOD(positionUpdated),
738 Q_JNI_NATIVE_METHOD(locationProvidersDisabled),
739 Q_JNI_NATIVE_METHOD(satelliteGpsUpdated),
740 Q_JNI_NATIVE_METHOD(locationProvidersChanged),
741 Q_JNI_NATIVE_METHOD(satelliteGnssUpdated)
742 })) {
743 __android_log_print(ANDROID_LOG_FATAL, logTag, "Failed to register native methods");
744 return false;
745 }
746
747 GET_AND_CHECK_STATIC_METHOD(providerListMethodId, "providerList", jintArray);
748 GET_AND_CHECK_STATIC_METHOD(lastKnownPositionMethodId, "lastKnownPosition",
749 QtJniTypes::Location, bool, bool);
750 GET_AND_CHECK_STATIC_METHOD(startUpdatesMethodId, "startUpdates", jint, jint, jint, jint,
751 bool);
752 GET_AND_CHECK_STATIC_METHOD(stopUpdatesMethodId, "stopUpdates", void, jint);
753 GET_AND_CHECK_STATIC_METHOD(requestUpdateMethodId, "requestUpdate", jint, jint, jint, jint,
754 bool);
755 GET_AND_CHECK_STATIC_METHOD(startSatelliteUpdatesMethodId, "startSatelliteUpdates",
756 jint, jint, jint, bool);
757
758 return true;
759}
760
761Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM * /*vm*/, void * /*reserved*/)
762{
763 static bool initialized = false;
764 if (initialized)
765 return JNI_VERSION_1_6;
766 initialized = true;
767
768 __android_log_print(ANDROID_LOG_INFO, logTag, "Positioning start");
769
770 const auto context = QNativeInterface::QAndroidApplication::context();
771 QtJniTypes::QtPositioning::callStaticMethod<void>("setContext", context);
772
773 if (!registerNatives()) {
774 __android_log_print(ANDROID_LOG_FATAL, logTag, "registerNatives() failed");
775 return -1;
776 }
777
778 if (!ConstellationMapper::init()) {
779 __android_log_print(ANDROID_LOG_ERROR, logTag,
780 "Failed to extract constellation type constants. "
781 "Satellite system will be undefined!");
782 }
783
784 return JNI_VERSION_1_6;
785}
#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 void satelliteGpsUpdated(JNIEnv *env, jobject thiz, jobjectArray satellites, jint androidClassKey, jboolean isSingleUpdate)
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)
QList< QGeoSatelliteInfo > satelliteInfoFromJavaLocation(JNIEnv *jniEnv, jobjectArray satellites, QList< QGeoSatelliteInfo > *usedInFix)
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")