Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Programatically connected android device to Hotspot (no internet) switches back to wifi with internet

Tags:

android

Sorry for my english. I am writing a code to connect to another android device hotspot. It gets connected. But, the hotspot will have no internet in my case. Now the connected device, switches back to another wifi network with internet. Is there any better way to connect to hotspot other than my way? My code is below:

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.layout);
    AppCompatButton btnConnect=findViewById(R.id.btnConnect);
    btnConnect.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            registerReceiver(mWifiBroadcastReceiver , new IntentFilter("android.net.wifi.STATE_CHANGE"));
            EditText eSSID=findViewById(R.id.ssid);
            EditText ePassword=findViewById(R.id.password);
            String ssid = eSSID.getText().toString();
            String key = ePassword.getText().toString();
            WifiConfiguration wifiConfig = new WifiConfiguration();
            wifiConfig.SSID = String.format("\"%s\"" , ssid);
            wifiConfig.preSharedKey = String.format("\"%s\"" , key);
            connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
            wifiManager = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE);

            int netId = wifiManager.addNetwork(wifiConfig);
            wifiManager.disconnect();
            wifiManager.enableNetwork(netId , true);
            wifiManager.reconnect();
        }
    });


}

And the receiver is:

  private BroadcastReceiver mWifiBroadcastReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(final Context context , Intent intent) {
        switch (intent.getAction()) {
            case WifiManager.NETWORK_STATE_CHANGED_ACTION:
                NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
                boolean isConnected = info.isConnected();
                boolean isConnecting = info.isConnectedOrConnecting();

                //TODO: probably better to use the EXTRA_ info here
                String ssid = wifiManager.getConnectionInfo() != null ?
                        wifiManager.getConnectionInfo().getSSID() : null;


                ssid = normalizeAndroidWifiSsid(ssid);

                String stateName = "";
                switch (info.getState()) {
                    case CONNECTED:
                        stateName = "connected";
                        break;

                    case CONNECTING:
                        stateName = "connecting";
                        break;

                    case DISCONNECTED:
                        stateName = "disconnected";
                        break;

                    case DISCONNECTING:
                        stateName = "disconnecting";
                        break;

                    case SUSPENDED:
                        stateName = "suspended";
                        break;

                    case UNKNOWN:
                        stateName = "unknown";
                        break;
                }


                if (Build.VERSION.SDK_INT >= 21) {
                    if (isConnected) {
                        NetworkRequest.Builder builder = new NetworkRequest.Builder();
                        builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
                        builder.addCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
                        final String connectedSsid = ssid;
                        connectivityManager.registerNetworkCallback(builder.build() , new ConnectivityManager.NetworkCallback() {
                            @Override
                            public void onAvailable(Network network) {
                                super.onAvailable(network);
                                NetworkInfo networkInfo = connectivityManager.getNetworkInfo(network);

                                //This is always the SSID if it's wifi, even though this is *not* documented
                                String networkSsid = networkInfo.getExtraInfo();

                                if (networkSsid.equals(connectedSsid)) {
                                    runOnUiThread(new Runnable() {
                                        @Override
                                        public void run() {
                                            Toast.makeText(context,"Connected Successfully",Toast
                                                    .LENGTH_LONG).show();
                                        }
                                    });

                                    /*
                                     * We can now use network.openURLConnection and network.getSocketFactory()
                                     * to communicate using the wifi network that has no Internet
                                     */
                                    connectivityManager.unregisterNetworkCallback(this);
                                }
                            }
                        });

                    }
                }

                break;
        }
    }
};
like image 455
StartCoding Avatar asked Oct 05 '18 18:10

StartCoding


People also ask

Why does my hotspot show connected but no Internet?

Go to Settings > Wi-Fi & network > Wi-Fi, tap the hotspot you are connected to, and then tap the pencil icon at the top. Step 2. Tap Advanced options, change DHCP to Static from IP settings, enter a custom IP like 192.168. 1.20 in the IP address field, and tap Save.

Can you use your Android phone as a hotspot while connected to Wi-Fi?

To turn your Android phone into a hotspot, go to Settings, then Mobile Hotspot & Tethering. Tap on Mobile Hotspot to turn it on, set the name of your network and set a password. You connect a computer or tablet to your phone's Wi-Fi hotspot just as you would connect to any other Wi-Fi network.

How do you check if Wi-Fi is on or off in Android programmatically?

Checking the state of WiFi can be done by obtaining an instance to the WiFi system service as below: WifiManager wifi = (WifiManager)getSystemService(Context. WIFI_SERVICE); from this, the method isWifiEnabled() can be used to determine if WiFi is enabled.

How do I create a Wi-Fi hotspot from a mobile already connected to Wi-Fi?

For a Wi-Fi connection, tap Wi-Fi hotspot and toggle it on. The hotspot name will be displayed on this screen. Tap Advanced > Hotspot password for the password. Pull down from the top of the screen and navigate to Settings > Network & Internet > Hotspot & tethering > Wi-Fi Hotspot, and toggle the switch to on.


2 Answers

Due to the asynchronous nature of WiFi handling in Android, I would focus on those 4 lines looking for a solution

        int netId = wifiManager.addNetwork(wifiConfig);
        wifiManager.disconnect();
        wifiManager.enableNetwork(netId , true);
        wifiManager.reconnect();

disconnect and reconnect calls are wrong here, they are only confusing internal wifi state machine. Invoking enableNetwork with parameter boolean attemptConnect set to true is enough to programmatically select wifi.

So just use:

    int netId = manager.addNetwork(wifiConfig);
    manager.enableNetwork(netId, true);

To be honest I've tried scan through the internal sources, but internal state machine is quite complex. If you are interested, you may look here for state machine and here to see how wifi config database is handled.

Also, to avoid having multiple configuration entries, before calling addNetwork, check already created configs to look for SSID you want to connect and only call enableNetwork.

    List<WifiConfiguration> networks = manager.getConfiguredNetworks();
    for(WifiConfiguration c : networks) {
        if (isThisWifiAppSpecific(c.SSID)) {
            manager.enableNetwork(c.networkId, true);
            return;
        }
    }

I'm using it in production and works fine. Never tested in API level lower than 24.

like image 122
Jakub Piskorz Avatar answered Sep 19 '22 01:09

Jakub Piskorz


Since you are able to connect to the hotspot, temporarily, the end problem is the switching on the phone to a "better" alternative network. While the answer https://stackoverflow.com/a/53249295/949224 will help tidy up your code when connecting to your specific hotspot, you should also look into disabling "smart network switching" and Wi-Fi auto-reconnect. The former is also referred to as poor network watchdog, and is monitoring the connectivity on your current network and will switch to LTE data if it is likely to improve, and the latter will change to another known Wi-Fi AP in the case that can offer better connectivity.

Smart network switch can be disabled in the advanced WiFi settings activity. It's not exposed as a Java API in Android and 3rd party apps are not designed to be given a relevant permission, plus different OEM suppliers have different custom implementations, therefore the best approach is to launch the right settings panel from your application. Here is an example:

public static void requestSmartNetworkSettings(Context ctx){
    final Intent i = new Intent(Settings.ACTION_WIFI_IP_SETTINGS);
    final PackageManager mgr = ctx.getPackageManager();
    if( mgr.resolveActivity(i, PackageManager.MATCH_DEFAULT_ONLY) != null)
    {
        ctx.startActivity(i);
    }
}

On some devices, Smart network switching can be disabled without the external settings dialog via ConnectivityManager.setProcessDefaultNetwork(Network net) but I have seen this is unreliable. I normally set this anyway and then also use the settings.

Wi-Fi auto-reconnect can be disabled manually in the WiFi settings activity by selecting known APs and unchecking the check-box.

A quick way to do this programmatically is to iterate through your known AP IDs in WifiManager and call WifiManager.disableNetwork(int netID). You can always enable these again programmatically after you want to end the Hotspot connection. That way the user should get their phone back as normal afterwards.

By the way, there are ways to check whether poor network avoidance (auto switching) is enabled via Java reflection, but it is cumbersome and so I don't think its worth it to post examples here.

like image 32
dr_g Avatar answered Sep 20 '22 01:09

dr_g