Android之防火墙功能的实现
16lz
2021-01-23
需求:可以控制某个应用访问WIFI或移动网络的功能。
Android自带防火墙原理是:一旦开启防火墙,所有的应用都不能访问网络(包括WIFI和移动网络),所以不能满足需求,故需要在原生基础上新增两个方法来实现该功能,方法声明在frameworks/base/core/java/android/os目录下的INetworkManagementService.aidl中:
interface INetworkManagementService{ . . . void setMobileDataUidRule(int uid, boolean allow); //根据应用的uid设置是否允许它访问移动网络 void setWifiDataUidRule(int uid, boolean allow); //根据应用的uid设置是否允许它访问WIFI网络 . . .}
方法的实现是在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(); // 校验调用者是否是系统uid 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> ", 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> ", 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 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+"; // MTK平台 op_wifi = "wlan0"; } else { op_3g = "rmnet+"; op_wifi = "wlan0"; } // 新建一个drop_wall链表 res |= execIptables(V4V6, "-w", "-N", "drop_wall", NULL); // 把新建的drop_wall链表添加到OUTPUT链表目录下 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(); }}
另外,网络端口的查看方式:
adb shellnetcfg
代码实现方式:
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); }}
更多相关文章
- Android开发设置Activity全屏与不全屏的方法
- Android实现页面跳转的几种方式(转贴)
- Android 平台的檔案讀寫方式
- Android 上传图片到后台的一直方式Base64的String形式
- Android中监听Home键的4种方法总结
- Android的数据存储方式