需求:可以控制某个应用访问WIFI或移动网络的功能。
Android自带防火墙原理是:一旦开启防火墙,所有的应用都不能访问网络(包括WIFI和移动网络),所以不能满足需求,故需要在原生基础上新增两个方法来实现该功能,方法声明在frameworks/base/core/Java/android/os目录下的INetworkManagementService.aidl中:
- interface INetworkManagementService
- {
- . . .
- void setMobileDataUidRule(int uid, boolean allow);
-
- void setWifiDataUidRule(int uid, boolean allow);
- . . .
- }
方法的实现是在frameworks/base/services/core/java/com/android/server目录下的NetworkManagementService.java中:
- public class NetworkManagementService extends INetworkManagementService.Stub implements Watchdog.Monitor {
- . . .
- @Override
- public void setMobileDataUidRule(int uid, boolean allow) {
- enforceSystemUid();
- try {
- mConnector.execute("firewall", "set_mobile_data_uid_rule", uid, rule);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
- }
- }
- @Override
- public void setWifiDataUidRule(int uid, boolean allow) {
- enforceSystemUid();
- try {
- mConnector.execute("firewall", "set_wifi_data_uid_rule", uid, rule);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
- }
- }
- . . .
- }
下面又通过socket通信方式调用到system/netd层的system/netd/server目录下的CommandListener.cpp中:
- CommandListener::CommandListener() : FrameworkListener("netd", true) {
- . . .
- sFirewallCtrl->initIptableFirewall();
- . . .
- }
-
- int CommandListener::FirewallCmd::runCommand(SocketClient *cli, int argc, char **argv) {
- . . .
- if (!strcmp(argv[1], "set_mobile_data_uid_rule")) {
- if (argc != 4) {
- cli->sendMsg(ResponseCode::CommandSyntaxError,
- "Usage: firewall set_mobile_data_uid_rule <1000> <allow|deny>",
- false);
- return 0;
- }
- int uid = atoi(argv[2]);
- FirewallRule rule = parseRule(argv[3]);
- int res = sFirewallCtrl->setMobileDataUidRule(uid, rule);
- return sendGenericOkFail(cli, res);
- }
- if (!strcmp(argv[1], "set_wifi_data_uid_rule")) {
- if (argc != 4) {
- cli->sendMsg(ResponseCode::CommandSyntaxError,
- "Usage: firewall set_wifi_data_uid_rule <1000> <allow|deny>",
- false);
- return 0;
- }
- int uid = atoi(argv[2]);
- FirewallRule rule = parseRule(argv[3]);
- int res = sFirewallCtrl->setWifiDataUidRule(uid, rule);
- return sendGenericOkFail(cli, res);
- }
- . . .
- }
上面代码又调用到system/netd/server目录下的FirewallController.cpp中:
- . . .
- #include <cutils/properties.h>
-
- const char* op_3g;
- const char* op_wifi;
-
- . . .
-
- int FirewallController::initIptableFirewall(void) {
- int res = 0;
- char property[PROPERTY_VALUE_MAX];
- property_get("ro.hardware", property, "qcom");
- if (strncmp("qcom", property, 4) == 0) {
- op_3g = "rmnet+";
- op_wifi = "wlan0";
- } else if (strncmp("mt", property, 2) == 0 || strncmp("MT", property, 2) == 0) {
- op_3g = "ccmni+";
- op_wifi = "wlan0";
- } else {
- op_3g = "rmnet+";
- op_wifi = "wlan0";
- }
-
- res |= execIptables(V4V6, "-w", "-N", "drop_wall", NULL);
-
- res |= execIptables(V4V6, "-w", "-A", "OUTPUT", "-j", "drop_wall", NULL);
- }
-
- int FirewallController::setMobileDataUidRule(int uid, FirewallRule rule) {
- char uidStr[16];
- sprintf(uidStr, "%d", uid);
- const char* op;
- if (rule == ALLOW) {
- op = "-D";
- } else {
- op = "-A";
- }
- int res = 0;
- res |= execIptables(V4V6, "-w", op, "drop_wall", "-o", op_3g, "-m", "owner",
- "--uid-owner", uidStr, "-j", "REJECT", NULL);
- return res;
- }
-
- int FirewallController::setWifiDataUidRule(int uid, FirewallRule rule) {
- char uidStr[16];
- sprintf(uidStr, "%d", uid);
- const char* op;
- if (rule == ALLOW) {
- op = "-D";
- } else {
- op = "-A";
- }
- int res = 0;
- res |= execIptables(V4V6, "-w", op, "drop_wall", "-o", op_wifi, "-m", "owner",
- "--uid-owner", uidStr, "-j", "REJECT", NULL);
- return res;
- }
- . . .
方法声明在system/netd/server目录下的FirewallController.h中:
- class FirewallController {
- public:
- . . .
- int initIptableFirewall(void);
- int setMobileDataUidRule(int, FirewallRule);
- int setWifiDataUidRule(int, FirewallRule);
- . . .
- }
到这里方法就添加完毕了,下面看调用,调用者必须是系统uid,要把aidl文件拷贝到android.os包下,由于NetworkManagementService是隐藏的,故需要使用方式的方式调用:
- public static void setUidNetworkState(int uid, boolean enabled) {
- try {
- Method method = Class.forName("android.os.ServiceManager").getMethod("getService", String.class);
- IBinder binder = (IBinder) method.invoke(null, new Object[] {"network_management"});
- INetworkManagementService service = INetworkManagementService.Stub.asInterface(binder);
- if (service != null) {
- service.setMobileDataUidRule(uid, enabled);
- service.setWifiDataUidRule(uid, enabled);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
另外,网络端口的查看方式:
代码实现方式:
- ConnectivityManager manager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
- Network[] networks = manager.getAllNetworks();
- for (Network item : networks) {
- NetworkInfo info = manager.getNetworkInfo(item);
- if (info.getType() == ConnectivityManager.TYPE_WIFI) {
- String wifi = manager.getLinkProperties(item)
- .getInterfaceName();
- System.out.println("zyf wifi:" + wifi);
- } else if (info.getType() == ConnectivityManager.TYPE_MOBILE) {
- String mobile = manager.getLinkProperties(item)
- .getInterfaceName();
- System.out.println("zyf mobile:" + mobile);
- }
- }