天天看點

android功能的實作,Android之防火牆功能的實作

需求:可以控制某個應用通路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);

}

}