@@ -91,45 +91,119 @@ int getIpAddress(JNIEnv *env, jobject wifiInfoObj) {
9191 jmethodID mid = env->GetMethodID (jclz, " getIpAddress" , " ()I" );
9292 return env->CallIntMethod (wifiInfoObj, mid);
9393}
94- #endif
9594
96- static bool isValidUsableIPv4 (const QHostAddress &address) {
97- return address.protocol () == QAbstractSocket::IPv4Protocol && !address.isLoopback () &&
98- !address.isNull () && address != QHostAddress::AnyIPv4;
99- }
95+ /*
96+ * Get the device's own IPv4 address via ConnectivityManager.getLinkProperties()
97+ * on the active network. Unlike QNetworkInterface (which reads netlink route
98+ * sockets and is blocked by SELinux for untrusted apps on modern Android) and
99+ * the deprecated WifiManager.getConnectionInfo().getIpAddress() (which returns
100+ * 0 on Android 10+ without extra permissions), this goes through the regular
101+ * ConnectivityManager system service and is not subject to either restriction.
102+ */
103+ QString getConnectivityManagerIp (JNIEnv *env, jobject jCtxObj) {
104+ qDebug () << " getConnectivityManagerIp...." ;
105+
106+ jclass jCtxClz = env->FindClass (" android/content/Context" );
107+ jfieldID fidConnService = env->GetStaticFieldID (jCtxClz, " CONNECTIVITY_SERVICE" , " Ljava/lang/String;" );
108+ jstring jstrConnService = (jstring)env->GetStaticObjectField (jCtxClz, fidConnService);
109+ jmethodID midGetSystemService =
110+ env->GetMethodID (jCtxClz, " getSystemService" , " (Ljava/lang/String;)Ljava/lang/Object;" );
111+ jobject connManager = env->CallObjectMethod (jCtxObj, midGetSystemService, jstrConnService);
112+ env->DeleteLocalRef (jCtxClz);
113+ env->DeleteLocalRef (jstrConnService);
114+ if (connManager == NULL ) {
115+ return QString ();
116+ }
100117
101- static QHostAddress findLocalInterfaceIPv4 () {
102- const auto interfaces = QNetworkInterface::allInterfaces ();
103- QHostAddress fallbackAddress;
118+ jclass connManagerClz = env->GetObjectClass (connManager);
119+ // getActiveNetwork() is only available on API 23+; on older devices this
120+ // GetMethodID throws NoSuchMethodError and returns NULL.
121+ jmethodID midGetActiveNetwork = env->GetMethodID (connManagerClz, " getActiveNetwork" , " ()Landroid/net/Network;" );
122+ if (midGetActiveNetwork == NULL ) {
123+ env->ExceptionClear ();
124+ env->DeleteLocalRef (connManagerClz);
125+ env->DeleteLocalRef (connManager);
126+ return QString ();
127+ }
128+ jobject network = env->CallObjectMethod (connManager, midGetActiveNetwork);
129+ env->DeleteLocalRef (connManager);
130+ if (network == NULL ) {
131+ env->DeleteLocalRef (connManagerClz);
132+ return QString ();
133+ }
104134
105- for ( const QNetworkInterface &networkInterface : interfaces) {
106- const auto flags = networkInterface. flags ( );
107- const bool interfaceIsUsable =
108- flags. testFlag (QNetworkInterface::IsUp) && flags. testFlag (QNetworkInterface::IsRunning) &&
109- !flags. testFlag (QNetworkInterface::IsLoopBack );
110- if (!interfaceIsUsable ) {
111- continue ;
112- }
135+ jmethodID midGetLinkProperties = env-> GetMethodID (
136+ connManagerClz, " getLinkProperties " , " (Landroid/net/Network;)Landroid/net/LinkProperties; " );
137+ jobject linkProperties = env-> CallObjectMethod (connManager, midGetLinkProperties, network);
138+ env-> DeleteLocalRef (connManagerClz);
139+ env-> DeleteLocalRef (network );
140+ if (linkProperties == NULL ) {
141+ return QString () ;
142+ }
113143
114- const auto entries = networkInterface.addressEntries ();
115- for (const QNetworkAddressEntry &entry : entries) {
116- const QHostAddress address = entry.ip ();
117- if (!isValidUsableIPv4 (address)) {
118- continue ;
119- }
144+ jclass linkPropertiesClz = env->GetObjectClass (linkProperties);
145+ jmethodID midGetLinkAddresses = env->GetMethodID (linkPropertiesClz, " getLinkAddresses" , " ()Ljava/util/List;" );
146+ jobject linkAddresses = env->CallObjectMethod (linkProperties, midGetLinkAddresses);
147+ env->DeleteLocalRef (linkPropertiesClz);
148+ env->DeleteLocalRef (linkProperties);
149+ if (linkAddresses == NULL ) {
150+ return QString ();
151+ }
120152
121- // Android Wi-Fi interfaces are usually wlan*. Prefer them when available.
122- if (networkInterface. name (). startsWith ( " wlan " , Qt::CaseInsensitive)) {
123- return address ;
124- }
153+ jclass listClz = env-> GetObjectClass (linkAddresses);
154+ jmethodID midSize = env-> GetMethodID (listClz, " size " , " ()I " );
155+ jmethodID midGet = env-> GetMethodID (listClz, " get " , " (I)Ljava/lang/Object; " ) ;
156+ const jint size = env-> CallIntMethod (linkAddresses, midSize);
125157
126- if (fallbackAddress.isNull ()) {
127- fallbackAddress = address;
128- }
158+ jclass linkAddressClz = env->FindClass (" android/net/LinkAddress" );
159+ jmethodID midGetAddress = env->GetMethodID (linkAddressClz, " getAddress" , " ()Ljava/net/InetAddress;" );
160+ jclass inet4Clz = env->FindClass (" java/net/Inet4Address" );
161+
162+ QString result;
163+ for (jint i = 0 ; i < size; ++i) {
164+ jobject linkAddress = env->CallObjectMethod (linkAddresses, midGet, i);
165+ if (linkAddress == NULL ) {
166+ continue ;
167+ }
168+ jobject inetAddress = env->CallObjectMethod (linkAddress, midGetAddress);
169+ env->DeleteLocalRef (linkAddress);
170+ if (inetAddress == NULL ) {
171+ continue ;
172+ }
173+ if (!env->IsInstanceOf (inetAddress, inet4Clz)) {
174+ env->DeleteLocalRef (inetAddress);
175+ continue ;
176+ }
177+
178+ jclass inetAddressClz = env->GetObjectClass (inetAddress);
179+ jmethodID midIsLoopback = env->GetMethodID (inetAddressClz, " isLoopbackAddress" , " ()Z" );
180+ const bool isLoopback = env->CallBooleanMethod (inetAddress, midIsLoopback);
181+ if (!isLoopback) {
182+ jmethodID midGetHostAddress = env->GetMethodID (inetAddressClz, " getHostAddress" , " ()Ljava/lang/String;" );
183+ jstring jHostAddress = (jstring)env->CallObjectMethod (inetAddress, midGetHostAddress);
184+ const char *hostAddressChars = env->GetStringUTFChars (jHostAddress, NULL );
185+ result = QString::fromUtf8 (hostAddressChars);
186+ env->ReleaseStringUTFChars (jHostAddress, hostAddressChars);
187+ env->DeleteLocalRef (jHostAddress);
188+ }
189+ env->DeleteLocalRef (inetAddressClz);
190+ env->DeleteLocalRef (inetAddress);
191+ if (!result.isEmpty ()) {
192+ break ;
129193 }
130194 }
131195
132- return fallbackAddress;
196+ env->DeleteLocalRef (linkAddressClz);
197+ env->DeleteLocalRef (inet4Clz);
198+ env->DeleteLocalRef (listClz);
199+ env->DeleteLocalRef (linkAddresses);
200+ return result;
201+ }
202+ #endif
203+
204+ static bool isValidUsableIPv4 (const QHostAddress &address) {
205+ return address.protocol () == QAbstractSocket::IPv4Protocol && !address.isLoopback () &&
206+ !address.isNull () && address != QHostAddress::AnyIPv4;
133207}
134208
135209QHostAddress localipaddress::getIP (const QHostAddress &srcAddress) {
@@ -154,13 +228,20 @@ QHostAddress localipaddress::getIP(const QHostAddress &srcAddress) {
154228 }
155229 }
156230#ifdef Q_OS_ANDROID
157- QHostAddress interfaceIp = findLocalInterfaceIPv4 ();
158- if (!interfaceIp.isNull ()) {
159- qDebug () << " getIP from interface scan" << interfaceIp;
160- return interfaceIp;
231+ QAndroidJniEnvironment env;
232+
233+ const QString connectivityIp = getConnectivityManagerIp (env, QtAndroid::androidContext ().object ());
234+ if (env->ExceptionCheck ()) {
235+ // getActiveNetwork()/getLinkProperties() require API 23+; ignore on older devices.
236+ env->ExceptionClear ();
237+ } else if (!connectivityIp.isEmpty ()) {
238+ QHostAddress connectivityAddress (connectivityIp);
239+ if (isValidUsableIPv4 (connectivityAddress)) {
240+ qDebug () << " getIP from ConnectivityManager" << connectivityAddress;
241+ return connectivityAddress;
242+ }
161243 }
162244
163- QAndroidJniEnvironment env;
164245 jobject wifiManagerObj = getWifiManagerObj (env, QtAndroid::androidContext ().object ());
165246 jobject wifiInfoObj = getWifiInfoObj (env, wifiManagerObj);
166247 int ip = getIpAddress (env, wifiInfoObj);
0 commit comments