Android TLS 套接字连接无法以编程方式添加的热点
Android TLS Socket connection not possible to programmatically added Hotspot
我目前正在实施两个应用程序:一个客户端应用程序,它通过 TLS 套接字连接与另一个服务器应用程序通信。服务器 phone (Android 7.1) 需要创建一个带密码的热点(可以在 Android 热点设置中设置)。如果我通过“Android 系统 Wi-Fi 面板”将客户端 phone (Android 10) 连接到热点(点击 Wi-Fi,select热点,输入密码→已连接)。我的Socket连接可以建立-客户端和服务器可以通信。
客户端phone报告,此热点内没有可用的互联网,这没关系,因为我不想访问它的互联网连接。
但现在,我想通过 WifiManager 直接连接到它,并使用以下代码自行创建 WifiConfiguration,无需 进入 Android 设置:
public static boolean saveAndConnectToWpaConfig(Context context, String ssidParam, String passphrase)
{
String ssidFormated = "\"" + ssidParam + "\"";
WifiConfiguration wifiConfiguration = new WifiConfiguration();
wifiConfiguration.SSID = ssidFormated;
wifiConfiguration.preSharedKey = "\"" + passphrase + "\"";
wifiConfiguration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
try {
WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
int networkId;
if (wifiManager != null) {
for (WifiConfiguration wifi :wifiManager.getConfiguredNetworks()) {
if(wifi.SSID.equals(ssidFormated)) {
wifiManager.removeNetwork(wifi.networkId);
break;
}
}
networkId = wifiManager.addNetwork(wifiConfiguration);
if (networkId != -1)
{
wifiManager.disconnect();
if(wifiManager.enableNetwork(networkId, true)) {
wifiManager.saveConfiguration();
return true;
}
}
}
} catch (Exception e) {
Logs.log(Logs.LogTypes.exception, "Can't connect to SSID: " + ssidParam, e);
}
return false;
}
一方面,这段代码有效。我可以连接到热点,Android 说没问题。另一方面,尝试创建我的套接字连接现在意外导致 createSocket() 方法中的 timeout:
socket = SSLUtility.getSocketFactory().createSocket(ip, port);
异常:
java.net.ConnectException: failed to connect to /192.168.43.1 (port 42223) from /:: (port 36660): connect failed: ETIMEDOUT (Connection timed out)
at libcore.io.IoBridge.connect(IoBridge.java:143)
at java.net.PlainSocketImpl.socketConnect(PlainSocketImpl.java:142)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:390)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:230)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:212)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:436)
at java.net.Socket.connect(Socket.java:621)
(SSLUtility 构建了一个套接字工厂,但在我看来,这在那种情况下并不重要,因为当我通过“Android 系统 Wifi 面板连接到 Wifi 时,这段代码已经可以工作了”)
我检查了两个连接的配置文件:
通过“Android 系统 Wifi 面板”进行工作热点连接:
ID: 99 SSID: "My_Hotsport_1234" PROVIDER-NAME: null BSSID: null FQDN: null PRIO: 16 HIDDEN: false PMF: false
NetworkSelectionStatus NETWORK_SELECTION_ENABLED
hasEverConnected: true
numAssociation 1
update millis:1601964892066
creation millis:1601964892066
trusted
macRandomizationSetting: 1
mRandomizedMacAddress: 02:00:00:00:00:00
KeyMgmt: WPA_PSK Protocols: WPA RSN
AuthAlgorithms: OPEN
PairwiseCiphers: TKIP CCMP
GroupCiphers: WEP40 WEP104 TKIP CCMP
GroupMgmtCiphers:
SuiteBCiphers:
PSK/SAE: *
Enterprise config:
eap NULL
phase2 "auth=NULL"
IP config:
IP assignment: DHCP
Proxy settings: NONE
cuid=1000 cname=android.uid.system:1000 luid=1000 lname=android.uid.system:1000 lcuid=1000 userApproved=USER_UNSPECIFIED noInternetAccessExpected=true
recentFailure: Association Rejection code: 0
samsungSpecificFlags:
semAutoWifiScore: 0
isVendorAp : false
recoverableRSSI: -200
inRecoverArea: false
disabledTime: 0
notInRangeTime: 0
validatedInternetAccess: false
skipInternetCheck: -1
notAskAgainCheck: false
nextTargetRssi: 0
isCaptivePortal: false
isAuthenticated: false
loginUrl: null
autoReconnect: 1
isRecommended: false
isHomeProviderNetwork: false
WapiCertIndex: 0
WapiPskType: 0
isWeChatAp : false
semMhsUserName :
entryRssi24GHz : -78
entryRssi5GHz : -75
以编程方式创建的热点连接:
ID: 100 SSID: "My_Hotsport_1234" PROVIDER-NAME: null BSSID: null FQDN: null PRIO: 17 HIDDEN: false PMF: false
NetworkSelectionStatus NETWORK_SELECTION_ENABLED
hasEverConnected: true
numAssociation 1
numNoInternetAccessReports 1
update millis:1601965132465
creation millis:1601965132465
trusted
macRandomizationSetting: 1
mRandomizedMacAddress: 02:00:00:00:00:00
KeyMgmt: WPA_PSK WPA_EAP Protocols: WPA RSN
AuthAlgorithms: OPEN
PairwiseCiphers: TKIP CCMP
GroupCiphers: WEP40 WEP104 TKIP CCMP
GroupMgmtCiphers:
SuiteBCiphers:
PSK/SAE: *
Enterprise config:
eap NULL
phase2 "auth=NULL"
IP config:
IP assignment: DHCP
Proxy settings: NONE
cuid=10463 cname=com.myCompany.myAppName luid=10463 lname=com.myCompany.myAppName lcuid=10463 userApproved=USER_UNSPECIFIED noInternetAccessExpected=false
recentFailure: Association Rejection code: 0
samsungSpecificFlags:
semAutoWifiScore: 0
isVendorAp : false
recoverableRSSI: -200
inRecoverArea: false
disabledTime: 0
notInRangeTime: 0
validatedInternetAccess: false
skipInternetCheck: -1
notAskAgainCheck: false
nextTargetRssi: 0
isCaptivePortal: false
isAuthenticated: false
loginUrl: null
autoReconnect: 1
isRecommended: false
isHomeProviderNetwork: false
WapiCertIndex: 0
WapiPskType: 0
isWeChatAp : false
semMhsUserName :
entryRssi24GHz : -78
entryRssi5GHz : -75
两者的区别是:
- 有一个
numNoInternetAccessReports
标志不在可用的自创建配置中
- 用户在第一个 Android 系统(通过系统设置)和第二个我的应用程序(自行创建的配置)
现在我的问题:
- 甚至可以创建一个 TLS 套接字连接到 不是 由 Android 系统添加的热点吗?
- 或者它应该工作,我需要在配置文件中手动设置“无互联网”标志?
- 我是否遗漏了配置中的其他内容?我尝试设置不同的算法和密钥管理,但没有成功。
- 如何记录创建套接字的 SSL 输出?在“普通”Java 应用程序中,我可以添加 JVM 标志
-Djavax.net.debug=all
来获取输出,这在 Android 中怎么可能?
我发现一个 Android 对话框打开,它通知我在 Android 设置中连接到该热点时没有使用该热点的互联网连接。使用我自己创建的配置,它不会出现。
终于明白了..问题确实是那一行:
cuid=10463 cname=com.myCompany.myAppName luid=10463 lname=com.myCompany.myAppName lcuid=10463 userApproved=USER_UNSPECIFIED noInternetAccessExpected=false
但这与添加热点的用户(我的应用程序或 Android 系统)无关,而是 noInternetAccessExpected
字段。不幸的是,手动连接热点不会触发“不再询问”对话框(仅在我的情况下?),该对话框表示设置此字段的热点内没有可用的互联网。我通过从 adb shell ping 服务器发现了它。在我关闭对话框之前,没有针回来,关闭对话框后,它起作用了。
但是您不能在配置文件中设置该字段,因为它没有 getter/setter -> 感觉反射是必要的:
String ssidFormated = "\"" + ssidParam + "\"";
WifiConfiguration wifiConfiguration = new WifiConfiguration();
wifiConfiguration.SSID = ssidFormated;
wifiConfiguration.preSharedKey = "\"" + passphrase + "\"";
wifiConfiguration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
try {
Field fieldname = WifiConfiguration.class.getField("noInternetAccessExpected");
if(fieldname != null) {
fieldname.set(wifiConfiguration, true);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
然后 voir la - 这就是诀窍。我认为通常应该弹出对话框,但在我的情况下它没有...
希望这对某人有所帮助!
我目前正在实施两个应用程序:一个客户端应用程序,它通过 TLS 套接字连接与另一个服务器应用程序通信。服务器 phone (Android 7.1) 需要创建一个带密码的热点(可以在 Android 热点设置中设置)。如果我通过“Android 系统 Wi-Fi 面板”将客户端 phone (Android 10) 连接到热点(点击 Wi-Fi,select热点,输入密码→已连接)。我的Socket连接可以建立-客户端和服务器可以通信。
客户端phone报告,此热点内没有可用的互联网,这没关系,因为我不想访问它的互联网连接。
但现在,我想通过 WifiManager 直接连接到它,并使用以下代码自行创建 WifiConfiguration,无需 进入 Android 设置:
public static boolean saveAndConnectToWpaConfig(Context context, String ssidParam, String passphrase)
{
String ssidFormated = "\"" + ssidParam + "\"";
WifiConfiguration wifiConfiguration = new WifiConfiguration();
wifiConfiguration.SSID = ssidFormated;
wifiConfiguration.preSharedKey = "\"" + passphrase + "\"";
wifiConfiguration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
try {
WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
int networkId;
if (wifiManager != null) {
for (WifiConfiguration wifi :wifiManager.getConfiguredNetworks()) {
if(wifi.SSID.equals(ssidFormated)) {
wifiManager.removeNetwork(wifi.networkId);
break;
}
}
networkId = wifiManager.addNetwork(wifiConfiguration);
if (networkId != -1)
{
wifiManager.disconnect();
if(wifiManager.enableNetwork(networkId, true)) {
wifiManager.saveConfiguration();
return true;
}
}
}
} catch (Exception e) {
Logs.log(Logs.LogTypes.exception, "Can't connect to SSID: " + ssidParam, e);
}
return false;
}
一方面,这段代码有效。我可以连接到热点,Android 说没问题。另一方面,尝试创建我的套接字连接现在意外导致 createSocket() 方法中的 timeout:
socket = SSLUtility.getSocketFactory().createSocket(ip, port);
异常:
java.net.ConnectException: failed to connect to /192.168.43.1 (port 42223) from /:: (port 36660): connect failed: ETIMEDOUT (Connection timed out)
at libcore.io.IoBridge.connect(IoBridge.java:143)
at java.net.PlainSocketImpl.socketConnect(PlainSocketImpl.java:142)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:390)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:230)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:212)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:436)
at java.net.Socket.connect(Socket.java:621)
(SSLUtility 构建了一个套接字工厂,但在我看来,这在那种情况下并不重要,因为当我通过“Android 系统 Wifi 面板连接到 Wifi 时,这段代码已经可以工作了”)
我检查了两个连接的配置文件:
通过“Android 系统 Wifi 面板”进行工作热点连接:
ID: 99 SSID: "My_Hotsport_1234" PROVIDER-NAME: null BSSID: null FQDN: null PRIO: 16 HIDDEN: false PMF: false
NetworkSelectionStatus NETWORK_SELECTION_ENABLED
hasEverConnected: true
numAssociation 1
update millis:1601964892066
creation millis:1601964892066
trusted
macRandomizationSetting: 1
mRandomizedMacAddress: 02:00:00:00:00:00
KeyMgmt: WPA_PSK Protocols: WPA RSN
AuthAlgorithms: OPEN
PairwiseCiphers: TKIP CCMP
GroupCiphers: WEP40 WEP104 TKIP CCMP
GroupMgmtCiphers:
SuiteBCiphers:
PSK/SAE: *
Enterprise config:
eap NULL
phase2 "auth=NULL"
IP config:
IP assignment: DHCP
Proxy settings: NONE
cuid=1000 cname=android.uid.system:1000 luid=1000 lname=android.uid.system:1000 lcuid=1000 userApproved=USER_UNSPECIFIED noInternetAccessExpected=true
recentFailure: Association Rejection code: 0
samsungSpecificFlags:
semAutoWifiScore: 0
isVendorAp : false
recoverableRSSI: -200
inRecoverArea: false
disabledTime: 0
notInRangeTime: 0
validatedInternetAccess: false
skipInternetCheck: -1
notAskAgainCheck: false
nextTargetRssi: 0
isCaptivePortal: false
isAuthenticated: false
loginUrl: null
autoReconnect: 1
isRecommended: false
isHomeProviderNetwork: false
WapiCertIndex: 0
WapiPskType: 0
isWeChatAp : false
semMhsUserName :
entryRssi24GHz : -78
entryRssi5GHz : -75
以编程方式创建的热点连接:
ID: 100 SSID: "My_Hotsport_1234" PROVIDER-NAME: null BSSID: null FQDN: null PRIO: 17 HIDDEN: false PMF: false
NetworkSelectionStatus NETWORK_SELECTION_ENABLED
hasEverConnected: true
numAssociation 1
numNoInternetAccessReports 1
update millis:1601965132465
creation millis:1601965132465
trusted
macRandomizationSetting: 1
mRandomizedMacAddress: 02:00:00:00:00:00
KeyMgmt: WPA_PSK WPA_EAP Protocols: WPA RSN
AuthAlgorithms: OPEN
PairwiseCiphers: TKIP CCMP
GroupCiphers: WEP40 WEP104 TKIP CCMP
GroupMgmtCiphers:
SuiteBCiphers:
PSK/SAE: *
Enterprise config:
eap NULL
phase2 "auth=NULL"
IP config:
IP assignment: DHCP
Proxy settings: NONE
cuid=10463 cname=com.myCompany.myAppName luid=10463 lname=com.myCompany.myAppName lcuid=10463 userApproved=USER_UNSPECIFIED noInternetAccessExpected=false
recentFailure: Association Rejection code: 0
samsungSpecificFlags:
semAutoWifiScore: 0
isVendorAp : false
recoverableRSSI: -200
inRecoverArea: false
disabledTime: 0
notInRangeTime: 0
validatedInternetAccess: false
skipInternetCheck: -1
notAskAgainCheck: false
nextTargetRssi: 0
isCaptivePortal: false
isAuthenticated: false
loginUrl: null
autoReconnect: 1
isRecommended: false
isHomeProviderNetwork: false
WapiCertIndex: 0
WapiPskType: 0
isWeChatAp : false
semMhsUserName :
entryRssi24GHz : -78
entryRssi5GHz : -75
两者的区别是:
- 有一个
numNoInternetAccessReports
标志不在可用的自创建配置中 - 用户在第一个 Android 系统(通过系统设置)和第二个我的应用程序(自行创建的配置)
现在我的问题:
- 甚至可以创建一个 TLS 套接字连接到 不是 由 Android 系统添加的热点吗?
- 或者它应该工作,我需要在配置文件中手动设置“无互联网”标志?
- 我是否遗漏了配置中的其他内容?我尝试设置不同的算法和密钥管理,但没有成功。
- 如何记录创建套接字的 SSL 输出?在“普通”Java 应用程序中,我可以添加 JVM 标志
-Djavax.net.debug=all
来获取输出,这在 Android 中怎么可能?
我发现一个 Android 对话框打开,它通知我在 Android 设置中连接到该热点时没有使用该热点的互联网连接。使用我自己创建的配置,它不会出现。
终于明白了..问题确实是那一行:
cuid=10463 cname=com.myCompany.myAppName luid=10463 lname=com.myCompany.myAppName lcuid=10463 userApproved=USER_UNSPECIFIED noInternetAccessExpected=false
但这与添加热点的用户(我的应用程序或 Android 系统)无关,而是 noInternetAccessExpected
字段。不幸的是,手动连接热点不会触发“不再询问”对话框(仅在我的情况下?),该对话框表示设置此字段的热点内没有可用的互联网。我通过从 adb shell ping 服务器发现了它。在我关闭对话框之前,没有针回来,关闭对话框后,它起作用了。
但是您不能在配置文件中设置该字段,因为它没有 getter/setter -> 感觉反射是必要的:
String ssidFormated = "\"" + ssidParam + "\"";
WifiConfiguration wifiConfiguration = new WifiConfiguration();
wifiConfiguration.SSID = ssidFormated;
wifiConfiguration.preSharedKey = "\"" + passphrase + "\"";
wifiConfiguration.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
wifiConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
try {
Field fieldname = WifiConfiguration.class.getField("noInternetAccessExpected");
if(fieldname != null) {
fieldname.set(wifiConfiguration, true);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
然后 voir la - 这就是诀窍。我认为通常应该弹出对话框,但在我的情况下它没有...
希望这对某人有所帮助!