summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIvan Solovev <ivan.solovev@qt.io>2023-01-10 15:41:57 +0100
committerIvan Solovev <ivan.solovev@qt.io>2023-01-13 15:06:23 +0100
commitc0c8901c9ef20da419f7d9ddaec779d88445299d (patch)
tree01343842483df1cf3becab6776ae7517a56a6dcb
parent93f40583fc5801281d7faf7159d4d68b82d8bfee (diff)
QtPositioning: add GnssStatus support for android plugin
This commit adds support for GnssStatus and GnssStatus.Callback API to detect satellite info changes since API Level 24. Note that we also need to retain the old GpsStatus API, because we support API Level 23 as well. This commit introduces an abstract PositioningLooperBase class on the Java side, and creates different subclasses for each of the APIs. Only one subclass is instantiated at each run, depending on the OS version. As a drive-by: removed an unused LocationManager variable during PositioningLooper refactoring. This commit is manually cherry-picked from dd4200e16b49224ad2ff0f39415cddf165561b79 and adapted to use plain JNI APIs, as there is no QJniObject and QJniEnvironment in Qt 5. The reason for cherry-pick is that the deprecated methods are removed in SDK 31, so satellite information does not work when settings ANDROID_TARGET_SDK_VERSION >= 31. Fixes: QTBUG-92965 Change-Id: I8af1c37d99b75fc9ad27b77b1befa4e1f8626803 Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
-rw-r--r--src/plugins/position/android/jar/src/org/qtproject/qt5/android/positioning/QtPositioning.java92
-rw-r--r--src/plugins/position/android/src/jnipositioning.cpp162
2 files changed, 232 insertions, 22 deletions
diff --git a/src/plugins/position/android/jar/src/org/qtproject/qt5/android/positioning/QtPositioning.java b/src/plugins/position/android/jar/src/org/qtproject/qt5/android/positioning/QtPositioning.java
index e1e7c1ed..a763c0e9 100644
--- a/src/plugins/position/android/jar/src/org/qtproject/qt5/android/positioning/QtPositioning.java
+++ b/src/plugins/position/android/jar/src/org/qtproject/qt5/android/positioning/QtPositioning.java
@@ -45,9 +45,12 @@ import android.location.GpsStatus;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
+import android.location.GnssStatus;
+import android.location.GnssStatus.Callback;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
+import android.os.Build;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
@@ -97,7 +100,7 @@ public class QtPositioning implements LocationListener
/* If true this class acts as satellite signal monitor rather than location monitor */
private boolean isSatelliteUpdate = false;
- private PositioningLooper looperThread;
+ private PositioningLooperBase looperThread;
static public void setContext(Context context)
{
@@ -404,7 +407,12 @@ public class QtPositioning implements LocationListener
public QtPositioning()
{
- looperThread = new PositioningLooper();
+ // Use GpsStatus for API Level <= 23 (version M and below) and
+ // GnssStatus for other API levels.
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M)
+ looperThread = new PositioningLooperGps();
+ else
+ looperThread = new PositioningLooperGnss();
}
public Looper looper()
@@ -438,13 +446,16 @@ public class QtPositioning implements LocationListener
}
}
- private class PositioningLooper extends Thread implements GpsStatus.Listener{
+ private abstract class PositioningLooperBase extends Thread
+ {
private boolean looperRunning;
private Looper posLooper;
private boolean isSatelliteLooper = false;
- private LocationManager locManager = null;
- private PositioningLooper()
+ abstract protected void addSatelliteInfoListener();
+ abstract protected void removeSatelliteInfoListener();
+
+ private PositioningLooperBase()
{
looperRunning = false;
}
@@ -454,13 +465,8 @@ public class QtPositioning implements LocationListener
Looper.prepare();
Handler handler = new Handler();
- if (isSatelliteLooper) {
- try {
- locationManager.addGpsStatusListener(this);
- } catch(Exception e) {
- e.printStackTrace();
- }
- }
+ if (isSatelliteLooper)
+ addSatelliteInfoListener();
posLooper = Looper.myLooper();
synchronized (this) {
@@ -475,7 +481,7 @@ public class QtPositioning implements LocationListener
public void quitLooper()
{
if (isSatelliteLooper)
- locationManager.removeGpsStatusListener(this);
+ removeSatelliteInfoListener();
looper().quit();
}
@@ -494,6 +500,26 @@ public class QtPositioning implements LocationListener
return posLooper;
}
+ }
+
+ private class PositioningLooperGps extends PositioningLooperBase implements GpsStatus.Listener
+ {
+ @Override
+ protected void addSatelliteInfoListener()
+ {
+ try {
+ locationManager.addGpsStatusListener(this);
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ protected void removeSatelliteInfoListener()
+ {
+ locationManager.removeGpsStatusListener(this);
+ }
+
@Override
public void onGpsStatusChanged(int event) {
switch (event) {
@@ -510,7 +536,7 @@ public class QtPositioning implements LocationListener
list.add(sat);
}
GpsSatellite[] sats = list.toArray(new GpsSatellite[list.size()]);
- satelliteUpdated(sats, nativeClassReference, isSingleUpdate);
+ satelliteGpsUpdated(sats, nativeClassReference, isSingleUpdate);
break;
case GpsStatus.GPS_EVENT_STARTED:
@@ -521,10 +547,46 @@ public class QtPositioning implements LocationListener
}
}
+ private class PositioningGnssListener extends GnssStatus.Callback
+ {
+ @Override
+ public void onSatelliteStatusChanged(GnssStatus status)
+ {
+ satelliteGnssUpdated(status, nativeClassReference, isSingleUpdate);
+ }
+ }
+
+ private class PositioningLooperGnss extends PositioningLooperBase
+ {
+ private PositioningGnssListener gnssListener;
+
+ private PositioningLooperGnss()
+ {
+ gnssListener = new PositioningGnssListener();
+ }
+
+ @Override
+ protected void addSatelliteInfoListener()
+ {
+ try {
+ locationManager.registerGnssStatusCallback(gnssListener);
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ protected void removeSatelliteInfoListener()
+ {
+ locationManager.unregisterGnssStatusCallback(gnssListener);
+ }
+ }
+
public static native void positionUpdated(Location update, int androidClassKey, boolean isSingleUpdate);
public static native void locationProvidersDisabled(int androidClassKey);
public static native void locationProvidersChanged(int androidClassKey);
- public static native void satelliteUpdated(GpsSatellite[] update, int androidClassKey, boolean isSingleUpdate);
+ public static native void satelliteGpsUpdated(GpsSatellite[] update, int androidClassKey, boolean isSingleUpdate);
+ public static native void satelliteGnssUpdated(GnssStatus update, int androidClassKey, boolean isSingleUpdate);
@Override
public void onLocationChanged(Location location) {
diff --git a/src/plugins/position/android/src/jnipositioning.cpp b/src/plugins/position/android/src/jnipositioning.cpp
index 8605bf14..a884909e 100644
--- a/src/plugins/position/android/src/jnipositioning.cpp
+++ b/src/plugins/position/android/src/jnipositioning.cpp
@@ -64,6 +64,58 @@ static const char logTag[] = "QtPositioning";
static const char classErrorMsg[] = "Can't find class \"%s\"";
static const char methodErrorMsg[] = "Can't find method \"%s%s\"";
+namespace {
+
+/*!
+ \internal
+ This class encapsulates satellite system types, as defined by Android
+ GnssStatus API. Initialize during JNI_OnLoad() by the init() method, from
+ the Java side, rather than hard-coding.
+*/
+class ConstellationMapper
+{
+public:
+ static bool init(JNIEnv *jniEnv)
+ {
+ if (QtAndroidPrivate::androidSdkVersion() > 23) {
+ jclass gnssStatusObject = jniEnv->FindClass("android/location/GnssStatus");
+ if (!gnssStatusObject)
+ return false;
+
+ jfieldID gpsFieldId = jniEnv->GetStaticFieldID(gnssStatusObject,
+ "CONSTELLATION_GPS", "I");
+ jfieldID glonassFieldId = jniEnv->GetStaticFieldID(gnssStatusObject,
+ "CONSTELLATION_GLONASS", "I");
+ if (!gpsFieldId || !glonassFieldId)
+ return false;
+
+ m_gpsId = jniEnv->GetStaticIntField(gnssStatusObject, gpsFieldId);
+ m_glonassId = jniEnv->GetStaticIntField(gnssStatusObject, glonassFieldId);
+ }
+ // no need to query it for API level <= 23
+ return true;
+ }
+
+ static QGeoSatelliteInfo::SatelliteSystem toSatelliteSystem(int constellationType)
+ {
+ if (constellationType == m_gpsId)
+ return QGeoSatelliteInfo::GPS;
+ else if (constellationType == m_glonassId)
+ return QGeoSatelliteInfo::GLONASS;
+
+ return QGeoSatelliteInfo::Undefined;
+ }
+
+private:
+ static int m_gpsId;
+ static int m_glonassId;
+};
+
+int ConstellationMapper::m_gpsId = -1;
+int ConstellationMapper::m_glonassId = -1;
+
+} // anonymous namespace
+
namespace AndroidPositioning {
typedef QMap<int, QGeoPositionInfoSourceAndroid * > PositionSourceMap;
typedef QMap<int, QGeoSatelliteInfoSourceAndroid * > SatelliteSourceMap;
@@ -362,6 +414,74 @@ namespace AndroidPositioning {
return sats;
}
+ QList<QGeoSatelliteInfo> satelliteInfoFromJavaGnssStatus(JNIEnv *jniEnv, jobject gnssStatus,
+ QList<QGeoSatelliteInfo>* usedInFix)
+ {
+ QList<QGeoSatelliteInfo> sats;
+
+ jclass statusClass = jniEnv->GetObjectClass(gnssStatus);
+ if (!statusClass)
+ return sats;
+
+ jmethodID satCountMethod = getCachedMethodID(jniEnv, statusClass,
+ "getSatelliteCount", "()I");
+ jmethodID sigStrengthMethod = getCachedMethodID(jniEnv, statusClass, "getCn0DbHz", "(I)F");
+ jmethodID constTypeMethod = getCachedMethodID(jniEnv, statusClass,
+ "getConstellationType", "(I)I");
+ jmethodID svIdMethod = getCachedMethodID(jniEnv, statusClass, "getSvid", "(I)I");
+ jmethodID azimuthMethod = getCachedMethodID(jniEnv, statusClass,
+ "getAzimuthDegrees", "(I)F");
+ jmethodID elevationMethod = getCachedMethodID(jniEnv, statusClass,
+ "getElevationDegrees", "(I)F");
+ jmethodID usedInFixMethod = getCachedMethodID(jniEnv, statusClass,
+ "usedInFix", "(I)Z");
+
+ if (!satCountMethod || !sigStrengthMethod || !constTypeMethod || !svIdMethod
+ || !azimuthMethod || !elevationMethod || !usedInFixMethod) {
+ jniEnv->DeleteLocalRef(statusClass);
+ return sats;
+ }
+
+ const int satellitesCount = jniEnv->CallIntMethod(gnssStatus, satCountMethod);
+ for (int i = 0; i < satellitesCount; ++i) {
+ QGeoSatelliteInfo info;
+
+ // signal strength - this is actually a carrier-to-noise density,
+ // but the values are very close to what was previously returned by
+ // getSnr() method of the GpsSatellite API.
+ const jfloat cn0 = jniEnv->CallFloatMethod(gnssStatus, sigStrengthMethod, i);
+ info.setSignalStrength(static_cast<int>(cn0));
+
+ // satellite system
+ const jint constellationType = jniEnv->CallIntMethod(gnssStatus, constTypeMethod, i);
+ info.setSatelliteSystem(ConstellationMapper::toSatelliteSystem(constellationType));
+
+ // satellite identifier
+ const jint svId = jniEnv->CallIntMethod(gnssStatus, svIdMethod, i);
+ info.setSatelliteIdentifier(svId);
+
+ // azimuth
+ const jfloat azimuth = jniEnv->CallFloatMethod(gnssStatus, azimuthMethod, i);
+ info.setAttribute(QGeoSatelliteInfo::Azimuth, static_cast<qreal>(azimuth));
+
+ // elevation
+ const jfloat elevation = jniEnv->CallFloatMethod(gnssStatus, elevationMethod, i);
+ info.setAttribute(QGeoSatelliteInfo::Elevation, static_cast<qreal>(elevation));
+
+ // Used in fix - true if this satellite is actually used in
+ // determining the position.
+ const jboolean inFix = jniEnv->CallBooleanMethod(gnssStatus, usedInFixMethod, i);
+
+ sats.append(info);
+
+ if (inFix)
+ usedInFix->append(info);
+ }
+
+ jniEnv->DeleteLocalRef(statusClass);
+ return sats;
+ }
+
QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly)
{
AttachedJNIEnv env;
@@ -562,11 +682,10 @@ static void locationProvidersChanged(JNIEnv *env, jobject /*thiz*/, jint android
QMetaObject::invokeMethod(source, "locationProvidersChanged", Qt::AutoConnection);
}
-static void satelliteUpdated(JNIEnv *env, jobject /*thiz*/, jobjectArray satellites, jint androidClassKey, jboolean isSingleUpdate)
+static void notifySatelliteInfoUpdated(const QList<QGeoSatelliteInfo> &inView,
+ const QList<QGeoSatelliteInfo> &inUse,
+ jint androidClassKey, jboolean isSingleUpdate)
{
- QList<QGeoSatelliteInfo> inUse;
- QList<QGeoSatelliteInfo> sats = AndroidPositioning::satelliteInfoFromJavaLocation(env, satellites, &inUse);
-
QGeoSatelliteInfoSourceAndroid *source = AndroidPositioning::idToSatSource()->value(androidClassKey);
if (!source) {
qWarning("satelliteUpdated: source == 0");
@@ -574,12 +693,34 @@ static void satelliteUpdated(JNIEnv *env, jobject /*thiz*/, jobjectArray satelli
}
QMetaObject::invokeMethod(source, "processSatelliteUpdateInView", Qt::AutoConnection,
- Q_ARG(QList<QGeoSatelliteInfo>, sats), Q_ARG(bool, isSingleUpdate));
+ Q_ARG(QList<QGeoSatelliteInfo>, inView), Q_ARG(bool, isSingleUpdate));
QMetaObject::invokeMethod(source, "processSatelliteUpdateInUse", Qt::AutoConnection,
Q_ARG(QList<QGeoSatelliteInfo>, inUse), Q_ARG(bool, isSingleUpdate));
}
+static void satelliteGpsUpdated(JNIEnv *env, jobject thiz, jobjectArray satellites,
+ jint androidClassKey, jboolean isSingleUpdate)
+{
+ Q_UNUSED(thiz);
+ QList<QGeoSatelliteInfo> inUse;
+ QList<QGeoSatelliteInfo> sats =
+ AndroidPositioning::satelliteInfoFromJavaLocation(env, satellites, &inUse);
+
+ notifySatelliteInfoUpdated(sats, inUse, androidClassKey, isSingleUpdate);
+}
+
+static void satelliteGnssUpdated(JNIEnv *env, jobject thiz, jobject gnssStatus,
+ jint androidClassKey, jboolean isSingleUpdate)
+{
+ Q_UNUSED(thiz);
+ QList<QGeoSatelliteInfo> inUse;
+ QList<QGeoSatelliteInfo> sats =
+ AndroidPositioning::satelliteInfoFromJavaGnssStatus(env, gnssStatus, &inUse);
+
+ notifySatelliteInfoUpdated(sats, inUse, androidClassKey, isSingleUpdate);
+}
+
#define FIND_AND_CHECK_CLASS(CLASS_NAME) \
clazz = env->FindClass(CLASS_NAME); \
@@ -598,8 +739,9 @@ if (!VAR) { \
static JNINativeMethod methods[] = {
{"positionUpdated", "(Landroid/location/Location;IZ)V", (void *)positionUpdated},
{"locationProvidersDisabled", "(I)V", (void *) locationProvidersDisabled},
- {"satelliteUpdated", "([Landroid/location/GpsSatellite;IZ)V", (void *)satelliteUpdated},
- {"locationProvidersChanged", "(I)V", (void *) locationProvidersChanged}
+ {"satelliteGpsUpdated", "([Landroid/location/GpsSatellite;IZ)V", (void *)satelliteGpsUpdated},
+ {"locationProvidersChanged", "(I)V", (void *) locationProvidersChanged},
+ {"satelliteGnssUpdated", "(Landroid/location/GnssStatus;IZ)V", (void *)satelliteGnssUpdated}
};
static bool registerNatives(JNIEnv *env)
@@ -650,6 +792,12 @@ Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /*reserved*/)
return -1;
}
+ if (!ConstellationMapper::init(env)) {
+ __android_log_print(ANDROID_LOG_ERROR, logTag,
+ "Failed to extract constellation type constants. "
+ "Satellite system will be undefined!");
+ }
+
javaVM = vm;
return JNI_VERSION_1_6;
}