diff --git a/src/plugins/networkinformation/android/jar/src/org/qtproject/qt/android/networkinformation/QtAndroidNetworkInformation.java b/src/plugins/networkinformation/android/jar/src/org/qtproject/qt/android/networkinformation/QtAndroidNetworkInformation.java index 0e89d23ab4..403286121e 100644 --- a/src/plugins/networkinformation/android/jar/src/org/qtproject/qt/android/networkinformation/QtAndroidNetworkInformation.java +++ b/src/plugins/networkinformation/android/jar/src/org/qtproject/qt/android/networkinformation/QtAndroidNetworkInformation.java @@ -48,12 +48,14 @@ import android.net.ConnectivityManager.NetworkCallback; import android.net.NetworkRequest; import android.net.NetworkCapabilities; import android.net.Network; +import android.os.Build; public class QtAndroidNetworkInformation { private static final String LOG_TAG = "QtAndroidNetworkInformation"; private static native void connectivityChanged(); private static native void behindCaptivePortalChanged(boolean state); + private static native void transportMediumChanged(Transport transportMedium); private static QtNetworkInformationCallback m_callback = null; private static final Object m_lock = new Object(); @@ -62,8 +64,21 @@ public class QtAndroidNetworkInformation { 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 { public AndroidConnectivity previousState = null; + public Transport previousTransport = null; QtNetworkInformationCallback() { } @@ -77,13 +92,42 @@ public class QtAndroidNetworkInformation { s = AndroidConnectivity.Connected; else 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); + setTransportMedium(transport); final boolean captive = capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL); 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) { if (previousState != s) { previousState = s; @@ -91,6 +135,13 @@ public class QtAndroidNetworkInformation { } } + private void setTransportMedium(Transport t) { + if (previousTransport != t) { + previousTransport = t; + transportMediumChanged(t); + } + } + @Override public void onLost(Network network) { setState(AndroidConnectivity.Disconnected); @@ -112,10 +163,17 @@ public class QtAndroidNetworkInformation { ConnectivityManager manager = getConnectivityManager(context); m_callback = new QtNetworkInformationCallback(); 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); - 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); + } NetworkRequest request = builder.build(); + + // Can't use registerDefaultNetworkCallback because it doesn't let us know when + // the network disconnects! manager.registerNetworkCallback(request, m_callback); } } diff --git a/src/plugins/networkinformation/android/qandroidnetworkinformationbackend.cpp b/src/plugins/networkinformation/android/qandroidnetworkinformationbackend.cpp index 9eef471989..33103b00b7 100644 --- a/src/plugins/networkinformation/android/qandroidnetworkinformationbackend.cpp +++ b/src/plugins/networkinformation/android/qandroidnetworkinformationbackend.cpp @@ -65,13 +65,17 @@ public: static QNetworkInformation::Features featuresSupportedStatic() { 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; } private: Q_DISABLE_COPY_MOVE(QAndroidNetworkInformationBackend); + + void updateTransportMedium(AndroidConnectivityManager::AndroidTransport transport); + bool m_valid = false; }; @@ -129,6 +133,36 @@ QAndroidNetworkInformationBackend::QAndroidNetworkInformationBackend() connect(conman, &AndroidConnectivityManager::captivePortalChanged, this, &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 diff --git a/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.cpp b/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.cpp index e88fe7d955..281ad4a2a6 100644 --- a/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.cpp +++ b/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.cpp @@ -70,6 +70,15 @@ static void behindCaptivePortalChanged(JNIEnv *env, jobject obj, jboolean 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("ordinal"); + const auto transport = static_cast(value); + emit androidConnManagerInstance->connManager->transportMediumChanged(transport); +} + AndroidConnectivityManager::AndroidConnectivityManager() { if (!registerNatives()) @@ -132,11 +141,16 @@ bool AndroidConnectivityManager::registerNatives() if (!networkReceiver.isValid()) return false; + const QByteArray transportEnumSig = + QByteArray("(L") + networkInformationClass + "$Transport;)V"; + jclass clazz = env->GetObjectClass(networkReceiver.object()); static JNINativeMethod methods[] = { { "connectivityChanged", "()V", reinterpret_cast(networkConnectivityChanged) }, { "behindCaptivePortalChanged", "(Z)V", - reinterpret_cast(behindCaptivePortalChanged) } + reinterpret_cast(behindCaptivePortalChanged) }, + { "transportMediumChanged", transportEnumSig.data(), + reinterpret_cast(transportMediumChangedCallback) }, }; const bool ret = (env->RegisterNatives(clazz, methods, std::size(methods)) == JNI_OK); env->DeleteLocalRef(clazz); diff --git a/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.h b/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.h index 14f5aa9b57..790f6f3bd0 100644 --- a/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.h +++ b/src/plugins/networkinformation/android/wrapper/androidconnectivitymanager.h @@ -51,6 +51,20 @@ class AndroidConnectivityManager : public QObject public: enum class AndroidConnectivity { Connected, Unknown, Disconnected }; 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(); ~AndroidConnectivityManager(); @@ -60,6 +74,7 @@ public: Q_SIGNALS: void connectivityChanged(); void captivePortalChanged(bool state); + void transportMediumChanged(AndroidTransport transport); private: friend struct AndroidConnectivityManagerInstance;