Skip to content

Commit a91d032

Browse files
committed
Replace QNetworkInterface fallback with ConnectivityManager for Android IP
QNetworkInterface enumeration relies on netlink route sockets, which SELinux denies to untrusted (third-party) apps on modern Android (observed as nlmsg_readpriv AVC denials), so it was returning no usable interface at all on affected devices. Query the device's IPv4 address via ConnectivityManager.getLinkProperties() on the active network instead, which goes through the normal system service and isn't subject to that restriction. Falls back to the legacy WifiManager JNI path (still validated) on Android < 6.0 where getActiveNetwork() doesn't exist.
1 parent 1a8e224 commit a91d032

2 files changed

Lines changed: 117 additions & 35 deletions

File tree

src/android/AndroidManifest.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@
165165
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
166166
<uses-permission android:name="android.permission.INTERNET" />
167167
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
168+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
168169
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
169170
<uses-permission android:name="com.android.vending.BILLING"/>
170171
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

src/localipaddress.cpp

Lines changed: 116 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -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

135209
QHostAddress 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

Comments
 (0)