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?
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:
- 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;
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); } }); }
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.
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);
来源:https://stackoverflow.com/questions/35152417/send-data-through-wifi-no-internet-when-mobile-data-on