需求:可以控制某個應用通路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("firewll", "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("firewll", "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 shell
netcfg
代碼實作方式:
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);
}
}