Search code examples
androidsocketssslandroid-wifiwifi

Android TLS Socket connection not possible to programmatically added Hotspot


I'm currently implementing two apps: a client app which communicates with another server app via TLS Socket connection. The server phone (Android 7.1) needs to create a hotspot with passphrase (can be set up in the Android hotspot settings). This connection works fine, if I connect the client phone (Android 10) to the hotspot via the "Android System Wi-Fi Panel" (tap on Wi-Fi, select hotspot, enter passphrase → connected). My Socket connection can be established - client and server can communicate.

The client phone reports, that there is no internet available within this hotspot, which is ok since I don't want to access its internet connection.

But now, I want to connect directly to it via the WifiManager and a self created WifiConfiguration by using the following code, without going to the Android settings:

    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;
}

On the one hand, this code works. I can connect to the hotspot and Android says it's fine. On the other hand, trying to create my socket connection leads now unexpected to a timeout in the createSocket() method:

socket = SSLUtility.getSocketFactory().createSocket(ip, port);

Exception:

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)

(The SSLUtility builds a socket factory, but in my opinion, this is not important in that case since this code already works when I connect to the Wifi via the "Android System Wifi Panel")

I inspected the configuration files of both connections:

Working Hotspot connection via "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

Programmatically created hotspot connection:

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

The two differences are:

  • There is a numNoInternetAccessReports flag which is not in the self created config available
  • The user is in the first one the Android System (via system settings) and in the second one my app (self created configuration)

Now my questions:

  • Is it even possible to create a TLS socket connection to a Hotspot which was not added by the Android System?
  • Or should it work and I need to set the "no internet" flag manually in the config file?
  • Did I miss something else in the configuration? I tried to set different algorithms and key managements without success.
  • How can I log the SSL output creating the socket? In a "normal" Java application, I can add the JVM flag -Djavax.net.debug=all to get the output, how is this in Android possible?

I recognized that an Android dialog opens, which notify that there is no internet connection using this hotspot when I'm connecting to it in the Android Settings. With my self created configuration, it won't appear.


Solution

  • Finally I got it.. The problem was indeed the line:

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

    But it's not regarding the user (my app or the Android system) who added the Hotspot, it's the noInternetAccessExpected field. Unfortunately, connecting the Hotspot manually doesn't trigger the "don't ask again" dialog (only in my case?) which says that there is no internet available within this hotspot which set this field. I discovered it by pinging the server from the adb shell. Before I closed the dialog, no pin came back, after closing the dialog, it worked.

    But you can't set the field in the configuration file since it has no getter/setter -> smells like reflection is necessary:

        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();
        }
    

    And voir la - this was the trick. I think normally the dialog should pop up, but in my case it didn't...

    Hope this could help someone!