setProcessDefaultNetwork()接口介绍

setProcessDefaultNetwork()接口介绍

android支持多种网络类型,例如WIFI,移动数据等。目前android的实现是,WIFI和移动数据只能同时存在一个(优先级),例如当WIFI连接后,数据通路就从移动数据切换到WIFI。对上层app而言,这时候数据通路也就从移动数据切换到WIFI上。

考虑一个特殊的需求,某app只能通过移动数据接口去传输数据或则当移动数据同时存在几个链路承载时,指定某一个特定的链路去传输数据,是否可以实现?android5.0及以后的版本添加了接口ConnectivityManager.setProcessDefaultNetwork()来支持这一功能.


1.该接口的详细描述如下:

/**

*Binds the current process to {@code network}. All Sockets created inthe future

*(and not explicitly bound via a bound SocketFactory from

*{@link Network#getSocketFactory() Network.getSocketFactory()}) willbe bound to

*{@code network}. All host name resolutions will be limited to {@codenetwork} as well.

*Note that if {@code network} ever disconnects, all Sockets created inthis way will cease to

*work and all host name resolutions will fail. This is by design soan application doesn't

*accidentally use Sockets it thinks are still bound to a particular{@link Network}.

*To clear binding pass {@code null} for {@code network}. Usingindividually bound

*Sockets created by Network.getSocketFactory().createSocket() and

*performing network-specific host name resolutions via

*{@link Network#getAllByName Network.getAllByName} is preferred tocalling

*{@code setProcessDefaultNetwork}.

*

*@param network The {@link Network} to bind the current process to, or{@code null} to clear

* the current binding.

*@return {@code true} on success, {@code false} if the {@link Network}is no longer valid.

*/

publicstatic boolean setProcessDefaultNetwork(Network network) {


}

2.该函数的实现原理大致为

a.进程在创建socket(app首先调用setProcessDefaultNetwork())android底层会利用setsockopt函数设置该socketSO_MARKnetIdandroid有自己的管理逻辑,每个Network有对应的ID),以后利用该socket发送的数据都会被打上netId的标记(fwmark)

b.利用策略路由,将打着netId标记的数据包都路由到当前链路的网络端口,下面是策略路由表的一个简单例子。


1|root@le_s2_na:/# ip rule list

0: fromall lookup local

10000: fromall fwmark 0xc0000/0xd0000 lookup legacy_system

10500: fromall oif dummy0 uidrange 0-0 lookup dummy0

10500: fromall oif rmnet_data0 uidrange 0-0 lookup rmnet_data0

13000: fromall fwmark 0x10063/0x1ffff lookup local_network

13000: fromall fwmark 0x1006a/0x1ffff lookup rmnet_data0

14000: fromall oif dummy0 lookup dummy0

14000: fromall oif rmnet_data0 lookup rmnet_data0

15000: fromall fwmark 0x0/0x10000 lookup legacy_system

16000: fromall fwmark 0x0/0x10000 lookup legacy_network

17000: fromall fwmark 0x0/0x10000 lookup local_network

19000: fromall fwmark 0x6a/0x1ffff lookup rmnet_data0

22000: fromall fwmark 0x0/0xffff lookup rmnet_data0

23000: fromall fwmark 0x0/0xffff uidrange 0-0 lookup main

32000: fromall unreachable

root@le_s2_na:/#


setProcessDefaultNetwork()的实现

publicstatic boolean setProcessDefaultNetwork(Network network) {

intnetId = (network == null) ? NETID_UNSET : network.netId;

if(netId == NetworkUtils.getNetworkBoundToProcess()) {

returntrue;

}

//调用bindProcessToNetwork()

if(NetworkUtils.bindProcessToNetwork(netId)) {

returntrue;

}else {

returnfalse;

}

}

setProcessDefaultNetwork最终会调用到netdNetdClient.cpp

extern "C" int setNetworkForProcess(unsigned netId) {

return setNetworkForTarget(netId, &netIdForProcess);  


下面函数主要作用是将当前链路的netid赋值给netIdForProcess,当应用建立socket的时候会用到该值

int setNetworkForTarget(unsigned netId, std::atomic_uint* target) {  

if (netId == NETID_UNSET) {  

*target = netId;  

        return 0;  

     }  

     // Verify that we are allowed to use |netId|, by creating a socket and trying to have it marked  

     // with the netId. Call libcSocket() directly; else the socket creation (via netdClientSocket())  

     // might itself cause another check with the fwmark server, which would be wasteful.  

    int socketFd;  

     if (libcSocket) {  

         socketFd = libcSocket(AF_INET6, SOCK_DGRAM, 0);  

     } else {  

         socketFd = socket(AF_INET6, SOCK_DGRAM, 0);  

     }  

     if (socketFd < 0) {  

      return -errno;  

 }  

 int error = setNetworkForSocket(netId, socketFd);  

     if (!error) {  

         *target = netId;  

     }  

 close(socketFd);  

      return error;  

 


Socket中连接中使用netIdForProcess

当应用建立socket的时候会调用到下面这个api

NetdClient.cpp

int netdClientSocket(int domain, int type, int protocol) {  

int socketFd = libcSocket(domain, type, protocol);  

if (socketFd == -1) {             

  return -1;  

  }  

unsigned netId = netIdForProcess;  

if (netId != NETID_UNSET && FwmarkClient::shouldSetFwmark(domain)) {   

  if (int error = setNetworkForSocket(netId, socketFd)) {  

  return closeFdAndSetErrno(socketFd, error);  

         }  

    }  

   return socketFd;                                                                                                                                                      

 }                

这里netIdForProcess就是前面设置下来的,因为它的值不再是NETID_UNSET,导致条件满足,调用到setNetworkForSocket


extern "C" int setNetworkForSocket(unsigned netId, int socketFd) {  

if (socketFd < 0) {  

        return -EBADF;  

      }  

     FwmarkCommand command = {FwmarkCommand::SELECT_NETWORK, netId, 0};  

      return FwmarkClient().send(&command, sizeof(command), socketFd);  

 }  

这个function最终将netId通过本地socket发送出去,结果由服务FwmakrServer接收并处理

 case FwmarkCommand::SELECT_NETWORK: {                                                                                                                

         fwmark.netId = command.netId;  

        if (command.netId == NETID_UNSET) {  

             fwmark.explicitlySelected = false;  

              fwmark.protectedFromVpn = false;  

                permission = PERMISSION_NONE;  

         } else {  

                if (int ret = mNetworkController->checkUserNetworkAccess(client->getUid(),  command.netId)) {  

                 return ret;  

}

               fwmark.explicitlySelected = true;  

                 fwmark.protectedFromVpn = mNetworkController->canProtect(client->getUid());  

          }  

          break;  

     }  

fwmark.permission = permission;                                                                                                                                       

    if (setsockopt(*socketFd, SOL_SOCKET, SO_MARK, &fwmark.intValue,  

     sizeof(fwmark.intValue)) == -1) {  

       return -errno;  

 }  

上面的处理中,首先会将netid赋值给fwmark,最后通过setsocketoptfwmark设置到socket中,这样就为建立的socket设置了mark

上面的处理中,首先会将netid赋值给fwmark,最后通过setsocketoptfwmark设置到socket中,这样就为建立的socket设置了mark从该socket发出的数据包都被netid的值标记。

下面就是看如何使用mark

下图列举了某一时刻一款android系统中的所有的路由规则



以下面这条路由规则为例:



0x10064
其中0x64(十进制为100)就是该网络的netid,如果应用选择在这个网络上建立socket,那么通过上面那些步骤建立的socket,就会被带有0x64mark,这样当改socket输出数据时候,就会被上面的路由规则捕获,从而使用eth0这个路由表。

下面是eth0路由表的内容

什么是netID

netID一个链路承载的标示,取值范围在<100,65535>之间,当请求建立链路承载时会调用下面函数选取一个合适的值

ConnectivityService.Java


@VisibleForTesting

protectedint reserveNetId() {

synchronized(mNetworkForNetId) {

for(int i = MIN_NET_ID; i <= MAX_NET_ID; i++) {

intnetId = mNextNetId;

if(++mNextNetId > MAX_NET_ID) mNextNetId = MIN_NET_ID;

//Make sure NetID unused. http://b/16815182

if(!mNetIdInUse.get(netId)) {

mNetIdInUse.put(netId,true);

returnnetId;

}

}

}

thrownew IllegalStateException("No free netIds");

}



  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值