在Android中使用NetworkRequest执行运行时命令

7

我正在我的Android应用程序中运行一个命令行参数,例如:

ProcessBuilder pb = new ProcessBuilder(cmds);
Process process = pb.start();
process.waitFor();

cmds是要运行的参数列表。 我的命令通过http连接探测远程URL。 我的设备连接到一个没有访问互联网但托管我想要探测的URL的WiFi网络。 我的设备还有一个具有访问互联网但不具有URL访问权限的蜂窝连接。 我的设备正在运行Android 6.0 Marshmallow。

通常情况下,在Lollipop或更高版本中,Android默认使用连接到互联网的网络。 要访问没有互联网的WiFi网络,您需要使用NetworkRequest,例如:https://dev59.com/i14d5IYBdhLWcg3wG_aH#27958106

我如何将获得的Network传递给上面的Process,以便连接通过我的WiFi网络而不是我的蜂窝网络进行?

我是否需要使用ConnectivityManager#bindProcessToNetwork? 我如何使用此方法加入进程以设置网络?似乎没有提供给进程的选项。


很难回答的问题。如果在构建和启动进程之前将网络绑定到应用程序进程,那么你能否如此幸运地继承网络呢? - totoro
很遗憾,似乎不是这样。 - Jon G
@JonG 你最终是如何解决这个问题的?你是否使用了建议的本地实现?我遇到了完全相同的问题,我能够绑定 API >= 21 的“普通”网络请求,但找不到一种绑定进程的方法(我还需要使用“ping”进行测试)。 - soey
@soey 你们解决问题了吗?我尝试在Java中设置默认网络,但没有成功。接下来,我使用正确的网络创建了Java Socket,并将描述符传递给本地子进程,但也没有成功。 - Himanshu
@Himanshu 抱歉,我没有找到解决方案。我认为这是一个非常特定的问题。最终我选择忽略它,例如在那种情况下不执行该进程,这对我的使用情况来说已经足够了。我记得在某个地方看到过一个解决方案,他们使用了本机套接字实现(用C编写),但我现在找不到它。而且我对使用本机代码并不是很有经验。 - soey
感谢Soey的更新。我尝试了@Michael答案中提到的本地实现,即将消息发送到fwmarkd以及netId和套接字fd。消息已经被发送到fwmarkd的Unix套接字,但无法从中接收任何内容。当不等待接收时,代码没有效果,并且流量从蜂窝网络传输。让我们看看是否还有其他可尝试的方法。 - Himanshu
1个回答

6
从Lollipop开始,Network是可以Parcelable的,因此您可以将其写入字节数组,然后进行读取。让我们从写入部分开始。
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();
}

还有阅读部分。

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

这段代码只能在Android 6.0上运行。如果你想在Lollipop上使用,你应该使用ConnectivityManager.setProcessDefaultNetwork(Network)代替ConnectivityManager.bindProcessToNetwork(Network)。而且这段代码不会在Android 5.0之前的设备上运行。
更新:
对于非Android进程,您可以创建一个套接字,使用Network.bindSocket(Socket)将其绑定到网络,并将套接字描述符传递给子进程。
如果以前的方法对您不起作用,您可以从multinetwork.h调用NDK函数android_setsocknetwork,甚至尝试并做Android在将进程绑定到给定网络时所做的一切。您可能感兴趣的所有内容都发生在netd client中。NetdClient这里fwmarkd发送一个带有网络ID的消息。实际的消息发送在这里发生。但我建议您将此方法作为解决问题的最后一种方式。

我不知道你可以打包一个网络,这很有趣,但我的进程在命令行上运行一个已编译的二进制文件,所以我不认为我可以将网络传递到那里。 - Jon G

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接