QNetworkInformation: add support for transportMedium on Android
It's part of the capabilities which we are already using. It also lets us work around a pre-existing edge-case where, if you have a VPN enabled and enable Airplane mode it will continue to tell you it is Online even when it is not. This happens because VPN is reported as a transport and when Airplane mode is enabled it may be left enabled as the _only_ transport. At the same time clear the default filters (if any), and filter out suspended connections. May not necessarily make any difference. And add a comment for why we cannot use a technically more suitable type of callback. Task-number: QTBUG-91023 Change-Id: Ic26c4d4e8da139ec8606a0b1bf5fb7157bd0beaf Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io> Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
parent
7fb855e175
commit
589389843c
@ -48,12 +48,14 @@ import android.net.ConnectivityManager.NetworkCallback;
|
|||||||
import android.net.NetworkRequest;
|
import android.net.NetworkRequest;
|
||||||
import android.net.NetworkCapabilities;
|
import android.net.NetworkCapabilities;
|
||||||
import android.net.Network;
|
import android.net.Network;
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
public class QtAndroidNetworkInformation {
|
public class QtAndroidNetworkInformation {
|
||||||
private static final String LOG_TAG = "QtAndroidNetworkInformation";
|
private static final String LOG_TAG = "QtAndroidNetworkInformation";
|
||||||
|
|
||||||
private static native void connectivityChanged();
|
private static native void connectivityChanged();
|
||||||
private static native void behindCaptivePortalChanged(boolean state);
|
private static native void behindCaptivePortalChanged(boolean state);
|
||||||
|
private static native void transportMediumChanged(Transport transportMedium);
|
||||||
|
|
||||||
private static QtNetworkInformationCallback m_callback = null;
|
private static QtNetworkInformationCallback m_callback = null;
|
||||||
private static final Object m_lock = new Object();
|
private static final Object m_lock = new Object();
|
||||||
@ -62,8 +64,21 @@ public class QtAndroidNetworkInformation {
|
|||||||
Connected, Unknown, Disconnected
|
Connected, Unknown, Disconnected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Keep synchronized with AndroidTransport in androidconnectivitymanager.h
|
||||||
|
enum Transport {
|
||||||
|
Unknown,
|
||||||
|
Bluetooth,
|
||||||
|
Cellular,
|
||||||
|
Ethernet,
|
||||||
|
LoWPAN,
|
||||||
|
Usb,
|
||||||
|
WiFi,
|
||||||
|
WiFiAware,
|
||||||
|
}
|
||||||
|
|
||||||
private static class QtNetworkInformationCallback extends NetworkCallback {
|
private static class QtNetworkInformationCallback extends NetworkCallback {
|
||||||
public AndroidConnectivity previousState = null;
|
public AndroidConnectivity previousState = null;
|
||||||
|
public Transport previousTransport = null;
|
||||||
|
|
||||||
QtNetworkInformationCallback() {
|
QtNetworkInformationCallback() {
|
||||||
}
|
}
|
||||||
@ -77,13 +92,42 @@ public class QtAndroidNetworkInformation {
|
|||||||
s = AndroidConnectivity.Connected;
|
s = AndroidConnectivity.Connected;
|
||||||
else
|
else
|
||||||
s = AndroidConnectivity.Unknown; // = we _may_ have Internet access
|
s = AndroidConnectivity.Unknown; // = we _may_ have Internet access
|
||||||
|
|
||||||
|
final Transport transport = getTransport(capabilities);
|
||||||
|
if (transport == Transport.Unknown) // If we don't have any transport media: override
|
||||||
|
s = AndroidConnectivity.Unknown;
|
||||||
|
|
||||||
setState(s);
|
setState(s);
|
||||||
|
setTransportMedium(transport);
|
||||||
|
|
||||||
final boolean captive
|
final boolean captive
|
||||||
= capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
|
= capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
|
||||||
behindCaptivePortalChanged(captive);
|
behindCaptivePortalChanged(captive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Transport getTransport(NetworkCapabilities capabilities)
|
||||||
|
{
|
||||||
|
if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
|
||||||
|
return Transport.WiFi;
|
||||||
|
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR)) {
|
||||||
|
return Transport.Cellular;
|
||||||
|
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH)) {
|
||||||
|
return Transport.Bluetooth;
|
||||||
|
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) {
|
||||||
|
return Transport.Ethernet;
|
||||||
|
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI_AWARE)) {
|
||||||
|
// Build.VERSION_CODES.O
|
||||||
|
return Transport.WiFiAware;
|
||||||
|
} else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_LOWPAN)) {
|
||||||
|
// Build.VERSION_CODES.O_MR1
|
||||||
|
return Transport.LoWPAN;
|
||||||
|
}/* else if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_USB)) {
|
||||||
|
// Build.VERSION_CODES.S
|
||||||
|
return Transport.Usb;
|
||||||
|
}*/ // @todo: Uncomment once we can use SDK 31
|
||||||
|
return Transport.Unknown;
|
||||||
|
}
|
||||||
|
|
||||||
private void setState(AndroidConnectivity s) {
|
private void setState(AndroidConnectivity s) {
|
||||||
if (previousState != s) {
|
if (previousState != s) {
|
||||||
previousState = s;
|
previousState = s;
|
||||||
@ -91,6 +135,13 @@ public class QtAndroidNetworkInformation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void setTransportMedium(Transport t) {
|
||||||
|
if (previousTransport != t) {
|
||||||
|
previousTransport = t;
|
||||||
|
transportMediumChanged(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLost(Network network) {
|
public void onLost(Network network) {
|
||||||
setState(AndroidConnectivity.Disconnected);
|
setState(AndroidConnectivity.Disconnected);
|
||||||
@ -112,10 +163,17 @@ public class QtAndroidNetworkInformation {
|
|||||||
ConnectivityManager manager = getConnectivityManager(context);
|
ConnectivityManager manager = getConnectivityManager(context);
|
||||||
m_callback = new QtNetworkInformationCallback();
|
m_callback = new QtNetworkInformationCallback();
|
||||||
NetworkRequest.Builder builder = new NetworkRequest.Builder();
|
NetworkRequest.Builder builder = new NetworkRequest.Builder();
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
|
||||||
|
builder = builder.clearCapabilities();
|
||||||
builder = builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
|
builder = builder.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET);
|
||||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P)
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
|
builder = builder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED);
|
||||||
builder = builder.addCapability(NetworkCapabilities.NET_CAPABILITY_FOREGROUND);
|
builder = builder.addCapability(NetworkCapabilities.NET_CAPABILITY_FOREGROUND);
|
||||||
|
}
|
||||||
NetworkRequest request = builder.build();
|
NetworkRequest request = builder.build();
|
||||||
|
|
||||||
|
// Can't use registerDefaultNetworkCallback because it doesn't let us know when
|
||||||
|
// the network disconnects!
|
||||||
manager.registerNetworkCallback(request, m_callback);
|
manager.registerNetworkCallback(request, m_callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,13 +65,17 @@ public:
|
|||||||
static QNetworkInformation::Features featuresSupportedStatic()
|
static QNetworkInformation::Features featuresSupportedStatic()
|
||||||
{
|
{
|
||||||
using Feature = QNetworkInformation::Feature;
|
using Feature = QNetworkInformation::Feature;
|
||||||
return QNetworkInformation::Features(Feature::Reachability | Feature::CaptivePortal);
|
return QNetworkInformation::Features(Feature::Reachability | Feature::CaptivePortal
|
||||||
|
| Feature::TransportMedium);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isValid() { return m_valid; }
|
bool isValid() { return m_valid; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Q_DISABLE_COPY_MOVE(QAndroidNetworkInformationBackend);
|
Q_DISABLE_COPY_MOVE(QAndroidNetworkInformationBackend);
|
||||||
|
|
||||||
|
void updateTransportMedium(AndroidConnectivityManager::AndroidTransport transport);
|
||||||
|
|
||||||
bool m_valid = false;
|
bool m_valid = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -129,6 +133,36 @@ QAndroidNetworkInformationBackend::QAndroidNetworkInformationBackend()
|
|||||||
|
|
||||||
connect(conman, &AndroidConnectivityManager::captivePortalChanged, this,
|
connect(conman, &AndroidConnectivityManager::captivePortalChanged, this,
|
||||||
&QAndroidNetworkInformationBackend::setBehindCaptivePortal);
|
&QAndroidNetworkInformationBackend::setBehindCaptivePortal);
|
||||||
|
|
||||||
|
connect(conman, &AndroidConnectivityManager::transportMediumChanged, this,
|
||||||
|
&QAndroidNetworkInformationBackend::updateTransportMedium);
|
||||||
|
}
|
||||||
|
|
||||||
|
void QAndroidNetworkInformationBackend::updateTransportMedium(
|
||||||
|
AndroidConnectivityManager::AndroidTransport transport)
|
||||||
|
{
|
||||||
|
using AndroidTransport = AndroidConnectivityManager::AndroidTransport;
|
||||||
|
using TransportMedium = QNetworkInformation::TransportMedium;
|
||||||
|
static const auto mapTransport = [](AndroidTransport state) -> TransportMedium {
|
||||||
|
switch (state) {
|
||||||
|
case AndroidTransport::Cellular:
|
||||||
|
return TransportMedium::Cellular;
|
||||||
|
case AndroidTransport::WiFi:
|
||||||
|
return TransportMedium::WiFi;
|
||||||
|
case AndroidTransport::Bluetooth:
|
||||||
|
return TransportMedium::Bluetooth;
|
||||||
|
case AndroidTransport::Ethernet:
|
||||||
|
return TransportMedium::Ethernet;
|
||||||
|
// These are not covered yet (but may be in the future)
|
||||||
|
case AndroidTransport::Usb:
|
||||||
|
case AndroidTransport::LoWPAN:
|
||||||
|
case AndroidTransport::WiFiAware:
|
||||||
|
case AndroidTransport::Unknown:
|
||||||
|
return TransportMedium::Unknown;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
setTransportMedium(mapTransport(transport));
|
||||||
}
|
}
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
@ -70,6 +70,15 @@ static void behindCaptivePortalChanged(JNIEnv *env, jobject obj, jboolean state)
|
|||||||
Q_EMIT androidConnManagerInstance->connManager->captivePortalChanged(state);
|
Q_EMIT androidConnManagerInstance->connManager->captivePortalChanged(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void transportMediumChangedCallback(JNIEnv *env, jobject obj, jobject enumValue)
|
||||||
|
{
|
||||||
|
Q_UNUSED(env);
|
||||||
|
Q_UNUSED(obj);
|
||||||
|
const jint value = QJniObject(enumValue).callMethod<jint>("ordinal");
|
||||||
|
const auto transport = static_cast<AndroidConnectivityManager::AndroidTransport>(value);
|
||||||
|
emit androidConnManagerInstance->connManager->transportMediumChanged(transport);
|
||||||
|
}
|
||||||
|
|
||||||
AndroidConnectivityManager::AndroidConnectivityManager()
|
AndroidConnectivityManager::AndroidConnectivityManager()
|
||||||
{
|
{
|
||||||
if (!registerNatives())
|
if (!registerNatives())
|
||||||
@ -132,11 +141,16 @@ bool AndroidConnectivityManager::registerNatives()
|
|||||||
if (!networkReceiver.isValid())
|
if (!networkReceiver.isValid())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
const QByteArray transportEnumSig =
|
||||||
|
QByteArray("(L") + networkInformationClass + "$Transport;)V";
|
||||||
|
|
||||||
jclass clazz = env->GetObjectClass(networkReceiver.object());
|
jclass clazz = env->GetObjectClass(networkReceiver.object());
|
||||||
static JNINativeMethod methods[] = {
|
static JNINativeMethod methods[] = {
|
||||||
{ "connectivityChanged", "()V", reinterpret_cast<void *>(networkConnectivityChanged) },
|
{ "connectivityChanged", "()V", reinterpret_cast<void *>(networkConnectivityChanged) },
|
||||||
{ "behindCaptivePortalChanged", "(Z)V",
|
{ "behindCaptivePortalChanged", "(Z)V",
|
||||||
reinterpret_cast<void *>(behindCaptivePortalChanged) }
|
reinterpret_cast<void *>(behindCaptivePortalChanged) },
|
||||||
|
{ "transportMediumChanged", transportEnumSig.data(),
|
||||||
|
reinterpret_cast<void *>(transportMediumChangedCallback) },
|
||||||
};
|
};
|
||||||
const bool ret = (env->RegisterNatives(clazz, methods, std::size(methods)) == JNI_OK);
|
const bool ret = (env->RegisterNatives(clazz, methods, std::size(methods)) == JNI_OK);
|
||||||
env->DeleteLocalRef(clazz);
|
env->DeleteLocalRef(clazz);
|
||||||
|
@ -51,6 +51,20 @@ class AndroidConnectivityManager : public QObject
|
|||||||
public:
|
public:
|
||||||
enum class AndroidConnectivity { Connected, Unknown, Disconnected };
|
enum class AndroidConnectivity { Connected, Unknown, Disconnected };
|
||||||
Q_ENUM(AndroidConnectivity);
|
Q_ENUM(AndroidConnectivity);
|
||||||
|
|
||||||
|
// Keep synchronized with Transport in QtAndroidNetworkInformation.java
|
||||||
|
enum class AndroidTransport {
|
||||||
|
Unknown,
|
||||||
|
Bluetooth,
|
||||||
|
Cellular,
|
||||||
|
Ethernet,
|
||||||
|
LoWPAN,
|
||||||
|
Usb,
|
||||||
|
WiFi,
|
||||||
|
WiFiAware,
|
||||||
|
};
|
||||||
|
Q_ENUM(AndroidTransport);
|
||||||
|
|
||||||
static AndroidConnectivityManager *getInstance();
|
static AndroidConnectivityManager *getInstance();
|
||||||
~AndroidConnectivityManager();
|
~AndroidConnectivityManager();
|
||||||
|
|
||||||
@ -60,6 +74,7 @@ public:
|
|||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void connectivityChanged();
|
void connectivityChanged();
|
||||||
void captivePortalChanged(bool state);
|
void captivePortalChanged(bool state);
|
||||||
|
void transportMediumChanged(AndroidTransport transport);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend struct AndroidConnectivityManagerInstance;
|
friend struct AndroidConnectivityManagerInstance;
|
||||||
|
Loading…
Reference in New Issue
Block a user