Android以太网开关流程分析


一、引言

  在Android 以太网服务启动流程中我们启动了EthernetService(ES),NetworkManagementService(NMS)等服务,其中ES作为Binder的服务端,向客户端EthernetManger提供了调用接口的引用。因此,应用可以通过EthernetManger来访问以太网的相关接口。本篇文章将从APP调用以太网的使能和关闭的接口流程作为切入点来分析,以太网框架中,上层app是如何将指令传递到底层。图1-1所示为以太网使能和关闭流程图

android 获取以太网的mac地址 安卓手机以太网_android


图1-1 以太网使能和关闭流程图

二、以太网使能流程

2.1 app端调用以太网的使能接口

  应用端调用以外网使能的接口是setEthernetEnabled,具体调用过如下:

import android.net.EthernetManager;
EthernetManager et;
//获取ethernet的Binder引用端
et = (EthernetManager)getSystemService("ethernet");
//调用setEthernetEnabled使能以太网
et.setEthernetEnabled("eth0", true);

  App端实现很简单,是Android 通常获取Binder通信过程,主要完成以下任务:

  • 获取以太网的Binder服务的引用,以便调用其使能接口;
  • 调用setEthernetEnabled接口来使能以外网

2.2 system_server进程的调用流程

App端调用setEthernetEnabled接口来使能以外网,具体代码如下:

//frameworks/base/core/java/android/net/EthernetManager.java 
public boolean setEthernetEnabled(String iface, boolean enabled) {
    Log.d(TAG,enabled ? "turn on Ethernet" : "turn off Ethernet");
    try {
        return mService.setEthernetEnabled(iface, enabled);
    } catch (RemoteException e) {
        return false;
    }
}

  其实调用很简单,直接调用服务端的setEthernetEnabled接口,需要注意的是,这里传入的iface为Hal层创建interface名称,以太网是eth0,enabled是true表示使能以太网,否则表示关闭以太网。下面我们直捣黄龙直接看服务端的setEthernetEnabled这个接口的实现。

//frameworks/opt/net/ethernet/java/com/android/server/ethernet/EthernetServiceImpl.java
public class EthernetServiceImpl extends IEthernetManager.Stub {
    //以太网服务的核心
    private EthernetTracker mTracker;
    public boolean setEthernetEnabled(String iface, boolean enable) {
       //enforceChangePermission();
       Log.i(TAG,"setEthernetEnabled() : enable="+enable);
      return mTracker.setEthernetEnabled(iface, enable);
    }
}

由Android 以太网服务启动流程我们可以知道EthernetTracker是以太网服务的核心,其能保存NetworkManagementService的引用对象,从而间接和Netd进行通信,下面我们直接看EthernetTracker中的setEthernetEnabled接口。

final class EthernetTracker { 
    private final INetworkManagementService mNMService;
     EthernetTracker(Context context, Handler handler) {
         IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
         mNMService = INetworkManagementService.Stub.asInterface(b);
     }

    public boolean setEthernetEnabled(String iface, boolean enable){
        try {
           if(!TextUtils.isEmpty(iface)) {
              
              if(!enable)
                  //enable为false,分别调用NMService的setInterfaceDown接口
                  mNMService.setInterfaceDown(iface);
              else
                  //enable为true,分别调用NMService的setInterfaceUp接口
                  mNMService.setInterfaceUp(iface);
              return true;
            } else {
                 Log.e(TAG,"iface is null");
            }
        } catch (Exception e) {
            Log.e(TAG, "Error setEthernetEnabled : " + iface + " enable : " 
              + enable + " exception : " + e);
        }
        return false;
        }
}

EthernetServiceImpl中相关实现很简单,主要完成以下三件事:

  • 创建INetworkManagementService引用对象,从而根据传入的enable的值,调用INetworkManagementService的相关方法;
  • 如果enable为true,则调用INetworkManagementService的setInterfaceUp接口;
  • 如果enable为false,则调用INetworkManagementService的setInterfaceDown接口;

此处是使能以太网,所以enable为true,所以调用setInterfaceUp接口,所以我们直接看NetworkManagementService的setInterfaceUp的接口。

public class NetworkManagementService extends INetworkManagementService.Stub { 
    //Netd的Binder引用对象
    private INetd mNetdService;
    //注册底层Netd的Listener
    private final NetdUnsolicitedEventListener mNetdUnsolicitedEventListener;
    //根据name获取Binder对象
    static class SystemServices {
        public IBinder getService(String name) {
            return ServiceManager.getService(name);
        }
        public void registerLocalService(NetworkManagementInternal nmi) {  
            LocalServices.addService(NetworkManagementInternal.class, nmi);
        }
        //获取Netd的引用对象
        public INetd getNetd() {
            return NetdService.get();
        }
    }
    static NetworkManagementService create(Context context, SystemServices services)
        throws InterruptedException {
          final NetworkManagementService service =
                    new NetworkManagementService(context, services);
          //调用connectNativeNetdService获取底层Netd的服务对象,并且注册Listener监听底层状态的
          //变化
          service.connectNativeNetdService();
         return service;
    }
    private void connectNativeNetdService() {
        //获取Netd的Binder引用对象
        mNetdService = mServices.getNetd();
        try {
            //注册Listener监听底层状态的变化
            mNetdService.registerUnsolicitedEventListener(mNetdUnsolicitedEventListener);
        } catch (RemoteException | ServiceSpecificException e) {
           Slog.e(TAG, "Failed to set Netd unsolicited event listener " + e);
       }
    }
    
    public void setInterfaceDown(String iface) {
        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
        Slog.d(TAG, "setInterfaceDown iface: " + iface);
        //先从Netd获取InterfaceConfiguration,然后设置器mFlag为up
        final InterfaceConfiguration ifcg = getInterfaceConfig(iface);
        ifcg.setInterfaceDown();
        //然后在设置将InterfaceConfiguration设置到底层
        setInterfaceConfig(iface, ifcg);
    }                                                                       
}

在NetworkMangementService中主要完成以下几件事:

  • 获取底层Netd的Binder服务端引用对象,调用Netd的相关接口;
  • 向Netd注册NetdUnsolicitedEventListener,当底层状态改变时候,通知上层;
  • setInterfaceDown接口实现也简单,首先通过Netd获取InterfaceConfiguration,然后将其mFlag标记为up,然后在设置到Netd中
public InterfaceConfiguration getInterfaceConfig(String iface) {
    mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    final InterfaceConfigurationParcel result;
    try {
        //通过Netd获取Interface的配置信息
        result = mNetdService.interfaceGetCfg(iface);
    } catch (RemoteException | ServiceSpecificException e) {
        throw new IllegalStateException(e);
    }
    try {
        //将result转为InterfaceConfiguration
        final InterfaceConfiguration cfg = fromStableParcel(result);                 
        return cfg;
    } catch (IllegalArgumentException iae) {
        throw new IllegalStateException("Invalid InterfaceConfigurationParcel", iae);
    }
}

public void setInterfaceConfig(String iface, InterfaceConfiguration cfg) {
    mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
    LinkAddress linkAddr = cfg.getLinkAddress();
    if (linkAddr == null || linkAddr.getAddress() == null) {
        throw new IllegalStateException("Null LinkAddress given");            
    }
    final InterfaceConfigurationParcel cfgParcel = toStableParcel(cfg, iface);
    try {
        //通过Netd设置interface的配置文件
        mNetdService.interfaceSetCfg(cfgParcel);
    } catch (RemoteException | ServiceSpecificException e) {
        throw new IllegalStateException(e);
    }
}

  从上面流程可以看出,调用Netd的interfaceGetCfg接口(传入参数为注册的网络名称,此处为eth0),获取Interface的Configuration;最后调用Netd的interfaceSetCfg的接口设置interface的Configuration。

2.3 Netd的处理流程

  从上一节可知,调用Netd的interfaceGetCfg接口(传入参数为注册的网络名称,此处为eth0),获取Interface的Configuration;最后调用Netd的interfaceSetCfg的接口设置interface的Configuration。

binder::Status NetdNativeService::interfaceGetCfg(
        const std::string& ifName, InterfaceConfigurationParcel* interfaceGetCfgResult) {
    NETD_LOCKING_RPC(InterfaceController::mutex, PERM_NETWORK_STACK, PERM_MAINLINE_NETWORK_STACK);
    auto entry = gLog.newEntry().prettyFunction(__PRETTY_FUNCTION__).arg(ifName);
    //通过InterfaceController的getCfg来获取cfgRes
    const auto& cfgRes = InterfaceController::getCfg(ifName);
    RETURN_BINDER_STATUS_IF_NOT_OK(entry, cfgRes);

    *interfaceGetCfgResult = cfgRes.value();
    gLog.log(entry.returns(interfaceConfigurationParcelToString(*interfaceGetCfgResult))          
                     .withAutomaticDuration());
    return binder::Status::ok();
}
binder::Status NetdNativeService::interfaceSetCfg(const InterfaceConfigurationParcel& cfg) {       
    NETD_LOCKING_RPC(InterfaceController::mutex, PERM_NETWORK_STACK, PERM_MAINLINE_NETWORK_STACK); 
    auto entry = gLog.newEntry()
                         .prettyFunction(__PRETTY_FUNCTION__)
                         .arg(interfaceConfigurationParcelToString(cfg));
  //通过InterfaceController的setCfg来设置cfg
    const auto& res = InterfaceController::setCfg(cfg);
    RETURN_BINDER_STATUS_IF_NOT_OK(entry, res);

    gLog.log(entry.withAutomaticDuration());
    return binder::Status::ok();
}

NetdNativeService中最后调用InterfaceContoller的getCfg和setCfg来处理,我们直接看这两个方法:

StatusOr<InterfaceConfigurationParcel> InterfaceController::getCfg(const std::string& ifName) {    
    struct in_addr addr = {};                                                                      
    int prefixLength = 0;                                                                          
    unsigned char hwaddr[ETH_ALEN] = {};                                                           
    unsigned flags = 0;                                                                            
    InterfaceConfigurationParcel cfgResult;                                                        
    //获取sys,并创建socket
    const auto& sys = sSyscalls.get();                                                             
    ASSIGN_OR_RETURN(auto fd, sys.socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));                  
                                                                                                   
    struct ifreq ifr = {};
    strlcpy(ifr.ifr_name, ifName.c_str(), IFNAMSIZ);
    //进行io流操作
    if (isOk(sys.ioctl(fd, SIOCGIFADDR, &ifr))) {                                                  
        addr.s_addr = ((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr;
    }

    if (isOk(sys.ioctl(fd, SIOCGIFNETMASK, &ifr))) {
        prefixLength =
                ipv4NetmaskToPrefixLength(((struct sockaddr_in*) &ifr.ifr_addr)->sin_addr.s_addr); 
    }

    if (isOk(sys.ioctl(fd, SIOCGIFFLAGS, &ifr))) {                                                 
        flags = ifr.ifr_flags;
    }

    // ETH_ALEN is for ARPHRD_ETHER, it is better to check the sa_family.                          
    // However, we keep old design for the consistency.                                            
    if (isOk(sys.ioctl(fd, SIOCGIFHWADDR, &ifr))) {                                                
        memcpy((void*) hwaddr, &ifr.ifr_hwaddr.sa_data, ETH_ALEN);                                 
    } else {
        ALOGW("Failed to retrieve HW addr for %s (%s)", ifName.c_str(), strerror(errno));          
    }

    cfgResult.ifName = ifName;
    cfgResult.hwAddr = hwAddrToStr(hwaddr);
    cfgResult.ipv4Addr = std::string(inet_ntoa(addr));
    cfgResult.prefixLength = prefixLength;
    cfgResult.flags.push_back(flags & IFF_UP ? toStdString(INetd::IF_STATE_UP())                   
                                             : toStdString(INetd::IF_STATE_DOWN()));               

    if (flags & IFF_BROADCAST) cfgResult.flags.push_back(toStdString(INetd::IF_FLAG_BROADCAST())); 
    if (flags & IFF_LOOPBACK) cfgResult.flags.push_back(toStdString(INetd::IF_FLAG_LOOPBACK()));   
    if (flags & IFF_POINTOPOINT)
        cfgResult.flags.push_back(toStdString(INetd::IF_FLAG_POINTOPOINT()));
    if (flags & IFF_RUNNING) cfgResult.flags.push_back(toStdString(INetd::IF_FLAG_RUNNING()));     
    if (flags & IFF_MULTICAST) cfgResult.flags.push_back(toStdString(INetd::IF_FLAG_MULTICAST())); 

    return cfgResult;
}

  getCfg的流程主要是创建和底层kernel的通信的socket,通过ioctrl的方式获取相关信息,最后封装为InterfaceConfigurationParcel传递给上层。

Status InterfaceController::setCfg(const InterfaceConfigurationParcel& cfg) {
    //获取socket流
    const auto& sys = sSyscalls.get();                                                    
    ASSIGN_OR_RETURN(auto fd, sys.socket(AF_INET, SOCK_DGRAM | SOCK_CLOEXEC, 0));         
    struct ifreq ifr = {                                                                  
            .ifr_addr = {.sa_family = AF_INET},  // Clear the IPv4 address.               
    };                                                                                    
    strlcpy(ifr.ifr_name, cfg.ifName.c_str(), IFNAMSIZ);                                  
                                                                                          
    // Make sure that clear IPv4 address before set flag                                  
    // SIOCGIFFLAGS might override ifr and caused clear IPv4 addr ioctl error             
    RETURN_IF_NOT_OK(sys.ioctl(fd, SIOCSIFADDR, &ifr));                                   
                                                                                          
    if (!cfg.flags.empty()) {                                                             
        RETURN_IF_NOT_OK(sys.ioctl(fd, SIOCGIFFLAGS, &ifr));                              
        uint16_t flags = ifr.ifr_flags;                                                   
                                                                                          
        for (const auto& flag : cfg.flags) {                                              
            if (flag == toStdString(INetd::IF_STATE_UP())) {                              
                ifr.ifr_flags = ifr.ifr_flags | IFF_UP;                                   
            } else if (flag == toStdString(INetd::IF_STATE_DOWN())) {                     
                ifr.ifr_flags = (ifr.ifr_flags & (~IFF_UP));                              
            }                                                                             
        }                                                                                 
                                                                                          
        if (ifr.ifr_flags != flags) {                                                     
            RETURN_IF_NOT_OK(sys.ioctl(fd, SIOCSIFFLAGS, &ifr));                          
        }                                                                                 
    }

    RETURN_STATUS_IF_IFCERROR(                                                            
            ifc_add_address(cfg.ifName.c_str(), cfg.ipv4Addr.c_str(), cfg.prefixLength)); 
                                                                                          
    return ok;                                                                            
}

  setCfg的流程和getCfg类似,先获取socket流,然后通过ioctrl设置interface configuration。

  至此以太网的使能流程就分析完了,Netd通过ioctrl从kernel获取interface confiuration;最后通过ioctrl将标记后的interface confiuration 设置到kernel,这样kernel将会触发interface的add和注册流程。

三、以太网关闭流程

  以外网的关闭流程和使能流程类似,只是传入的enable参数为false。在NetworkManagementService中,如果enable为false,则调用setInterfaceDown。可以看出其和setInterfaceUp流程很相似。不通的地方是此处获取到InterfaceConfiguration后,将mFlag标记为down。

public void setInterfaceDown(String iface) {                             
    mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); 
    Slog.d(TAG, "setInterfaceDown iface: " + iface);                     
    final InterfaceConfiguration ifcg = getInterfaceConfig(iface);       
    ifcg.setInterfaceDown();                                             
    setInterfaceConfig(iface, ifcg);                                     
}

四、总结

  这篇文章就写到这,下一篇将底层网线的插拔来说明,底层的状态是如何传递到上层的,敬请期待,谢谢大家关注。