9

I'm developing and application that connects to a hardware device through wifi (generated by the device) and send data to it through a socket connection. The problem is that when mobile data (3G/4G) is activated android tries to send the data through it instead of sending it through the wifi generated by the device, because because the wifi has no internet connection. I was thinking of using ConnectivityManager#setNetworkPreference() but it has been deprecated in api 21.

How can I set it to send data using the wifi generated by the device instead of the mobile data interface?

hygorxaraujo
  • 659
  • 6
  • 19

2 Answers2

16

After a lot of research and time trying to understand it all I found out that this isn't possible in versions previous to Android 5.0 (Lollipop), the OS only keeps one network interface up at a time and the apps don't have control over this.

So to do this on versions greater or equal to the Lollipop I did the following:

  1. Before doing your socket connection check if android greater or equal to Lollipop, in case it isn't you just do whatever you have to do normally;
  2. In case the version is equal or greater to Lollipop you need to do something like this:

    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
        final ConnectivityManager manager = (ConnectivityManager) context
                .getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkRequest.Builder builder;
        builder = new NetworkRequest.Builder();
        //set the transport type do WIFI
        builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
        manager.requestNetwork(builder.build(), new ConnectivityManager.NetworkCallback() {
            @Override
            public void onAvailable(Network network) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    manager.bindProcessToNetwork(network);
                } else {
                    //This method was deprecated in API level 23
                    ConnectivityManager.setProcessDefaultNetwork(network);
                }
                try {
                    //do a callback or something else to alert your code that it's ok to send the message through socket now
                } catch (Exception e) {
                    e.printStackTrace();
                }
                manager.unregisterNetworkCallback(this);
            }
        });
    }
    
  3. After you finish you should stop binding the process with this:

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        ConnectivityManager manager = (ConnectivityManager) InovePlugApplication.getContext()
                .getSystemService(Context.CONNECTIVITY_SERVICE);
        manager.bindProcessToNetwork(null);
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        ConnectivityManager.setProcessDefaultNetwork(null);
    }
    

This answer helped me unsderstand how to do it https://stackoverflow.com/a/29837637/2550932.

Community
  • 1
  • 1
hygorxaraujo
  • 659
  • 6
  • 19
  • 1
    You will need `` in your manifest for this to work. – AppleGrew Sep 17 '16 at 09:20
  • @hygorxaraujo I am using your code, but this is not working for android nougat. I am doing manager.bindProcessToNetwork(network) for Marshamallow and higher. is there any workaround? – Satpal Yadav Jun 23 '17 at 12:08
  • @satpal002 sorry, I only used this code until Android Marshmallow (and I do not have a device with Nougat to test it). I didn't notice any changes in the API for it to stop working https://developer.android.com/reference/android/net/ConnectivityManager.html. Is it working normally in the previous versions (above Lollipop)? – hygorxaraujo Jun 24 '17 at 17:18
  • yes, it is working fine in marshamallow, but on nougat: I am sending a socket message to an ip, then it gives an exception that network is unreachable (mobile data is switched on and also device is connected to wifi, and that ip is available on the same wifi network). – Satpal Yadav Jun 26 '17 at 08:38
0

Network will switch back to mobile after setProcessDefaultNetwork to wifi, you can't send large data by this way, because all sockets created in this way will cease to work. I don't find a perfect way now, here is for reference only.

1.If you can get root access, do this before connect to wifi:

Process process = runtime.exec("su");
DataOutputStream dataOutputStream = new DataOutputStream(process.getOutputStream());
dataOutputStream.writeBytes("settings put global captive_portal_server 127.0.0.1\n");
dataOutputStream.writeBytes("exit\n");
dataOutputStream.flush();
int status = process.waitFor();

And delete it after you send data:

dataOutputStream.writeBytes("settings delete global captive_portal_server\n");

2.If you can get WRITE_SECURE_SETTINGS permission:

Settings.Global.putString(getContentResolver(),"captive_portal_server","127.0.0.1");

Or:

Settings.Global.putInt(getContentResolver(),"captive_portal_detection_enabled",0);
MADAO
  • 11
  • 2
  • Hi @MADAO, thank you for your answer, I think you are also correct. I had already found an answer to this and forgot to update the question here. – hygorxaraujo Mar 12 '16 at 12:33