首页 > 解决方案 > Android TLS Socket 连接无法以编程方式添加热点

问题描述

我目前正在实现两个应用程序:一个客户端应用程序,它通过 TLS 套接字连接与另一个服务器应用程序通信。服务器手机(Android 7.1)需要创建一个带密码的热点(可以在Android热点设置中设置)。如果我通过“Android 系统 Wi-Fi 面板”将客户端手机(Android 10)连接到热点(点击 Wi-Fi,选择热点,输入密码短语 → 已连接),此连接工作正常。可以建立我的 Socket 连接 - 客户端和服务器可以通信。

客户端电话报告,此热点内没有可用的互联网,这没关系,因为我不想访问它的互联网连接。

但是现在,我想通过 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() 方法超时:

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 System Wifi Panel”进行热点连接:

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

这两个区别是:

现在我的问题

我意识到打开了一个 Android 对话框,当我在 Android 设置中连接到它时,它会通知没有使用此热点的互联网连接。使用我自己创建的配置,它不会出现。

标签: androidsocketssslandroid-wifiwifi

解决方案


终于我明白了..问题确实是这样的:

cuid=10463 cname=com.myCompany.myAppName luid=10463 lname=com.myCompany.myAppName lcuid=10463 userApproved=USER_UNSPECIFIED noInternetAccessExpected=false 

但这与添加 Hotspot 的用户(我的应用程序或 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();
    }

瞧,这就是诀窍。我认为通常应该弹出对话框,但在我的情况下它没有......

希望这可以帮助某人!


推荐阅读