I am running a command line argument in my Android application like:
ProcessBuilder pb = new ProcessBuilder(cmds);
Process process = pb.start();
process.waitFor();
Where cmds
are a list of arguments to run. My commands probe a remote URL over a http connection. My device is connected to a WiFi network that does not have access to the internet, but does host the URL I want to probe. My device also has a cellular connection that does have access to the internet, but not the URL. My device is running Android 6.0 Marshmallow.
Normally in Lollipop or above, Android defaults to the network with a connection to the internet. To access WiFi networks without internet you need to use NetworkRequest
, e.g: https://stackoverflow.com/a/27958106/1847734.
How can I pass an obtained Network
to the above Process
, so that the connection goes over my WiFi network, not my cellular network?
Do I instead need to use ConnectivityManager#bindProcessToNetwork? How do I join the process to set the network using this method? There doesn't seem to be an option to give the process.
Starting from Lollipop Network
is Parcelable
so you can write it to a byte array and then read back. Let's start from the writing part.
final Parcel parcel = Parcel.obtain();
try {
// Create a byte array from Network.
parcel.writeParcelable(network, 0);
final byte[] data = parcel.marshall();
// Start a process.
ProcessBuilder pb = new ProcessBuilder(cmds);
Process process = pb.start();
// Send serialized Network to the process.
final DataOutputStream out = new DataOutputStream(process.getOutputStream());
out.write(data.length);
out.write(data);
// Wait until the process terminates.
process.waitFor();
} finally {
parcel.recycle();
}
And the reading part.
// Read data from the input stream.
final DataInputStream in = new DataInputStream(System.in);
final int length = in.readInt();
final byte[] data = new byte[length];
in.readFully(data);
final Parcel parcel = Parcel.obtain();
try {
// Restore Network from a byte array.
parcel.unmarshall(data, 0, data.length);
final Network network = parcel.readParcelable(null);
// Use the Network object to bind the process to it.
connectivityManager.bindProcessToNetwork(network);
} finally {
parcel.recycle();
}
This code will work on Android 6.0 only. If you want it to work on Lollipop you should use ConnectivityManager.setProcessDefaultNetwork(Network)
instead of ConnectivityManager.bindProcessToNetwork(Network)
. And this code is not going to work on devices before Android 5.0.
UPDATE:
For a non-Android process you can create a socket, bind it to the nework with Network.bindSocket(Socket)
and pass a socket descriptor to the child process.
If the previous approach doesn't work for you, you can call NDK function android_setsocknetwork
from multinetwork.h
or even try and do what Android does when you bind a process to a given network. Everything you might be interested in happens in netd client. NetdClient
sends a message to fwmarkd
here passing a network id. Actual message sending happens here. But I would recommend to use this approach as the last chance way to solve your problem.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With