Android Netd分析

1.文章介绍

Netd模块是Android中专门负责网络管理和控制的后台守护进程,本篇文章主要分析Netd的工作流程,对Netd有一个在框架层次上的理解。

2.干货

1.Netd模块源码位置

源码位置根目录/system/netd/

2.入口函数

/system/netd/main.cpp

int main() {

    CommandListener *cl;
    NetlinkManager *nm;
    DnsProxyListener *dpl;
    MDnsSdListener *mdnsl;
    blockSigpipe();
    if (!(nm = NetlinkManager::Instance())) {
        ALOGE("Unable to create NetlinkManager");
        exit(1);
    };
    UidMarkMap *rangeMap = new UidMarkMap();
    cl = new CommandListener(rangeMap);
    nm->setBroadcaster((SocketListener *) cl);

    if (nm->start()) {
        ALOGE("Unable to start NetlinkManager (%s)", strerror(errno));
        exit(1);
    }
    setenv("ANDROID_DNS_MODE", "local", 1);
    dpl = new DnsProxyListener(rangeMap);
    if (dpl->startListener()) {
        ALOGE("Unable to start DnsProxyListener (%s)", strerror(errno));
        exit(1);
    }

    mdnsl = new MDnsSdListener();
    if (mdnsl->startListener()) {
        ALOGE("Unable to start MDnsSdListener (%s)", strerror(errno));
        exit(1);
    }
    /*
     * Now that we're up, we can respond to commands
     */
    if (cl->startListener()) {
        ALOGE("Unable to start CommandListener (%s)", strerror(errno));
        exit(1);
    }

    // Eventually we'll become the monitoring thread
    while(1) {
        sleep(1000);
    }

    ALOGI("Netd exiting");
    exit(0);
}

可以看出以下类的重要性:

    NetlinkManager *nm;
    CommandListener *cl;

DnsProxyListener、MDnsSdListener涉及到Android DNS模块的处理,本文暂不作分析。

1.NetlinkManager分析

首先了解下NetLink是什么,在linux下NetLink是一个异步通信机制的socket,区别于系统调用和ioctl的同步调用机制。
可以看到/system/netd/NetlinkManager .cpp这种懒汉单实例模式:

NetlinkManager *NetlinkManager::sInstance = NULL;
NetlinkManager *NetlinkManager::Instance() {
    if (!sInstance)
        sInstance = new NetlinkManager();
    return sInstance;
}

NetlinkHandler *NetlinkManager::setupSocket(int *sock, int netlinkFamily,
    int groups, int format) {

    struct sockaddr_nl nladdr;
    int sz = 64 * 1024;
    int on = 1;

    memset(&nladdr, 0, sizeof(nladdr));
    nladdr.nl_family = AF_NETLINK;
    nladdr.nl_pid = getpid();
    nladdr.nl_groups = groups;

    if ((*sock = socket(PF_NETLINK, SOCK_DGRAM, netlinkFamily)) < 0) {
        ALOGE("Unable to create netlink socket: %s", strerror(errno));
        return NULL;
    }
    if (setsockopt(*sock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {
        ALOGE("Unable to set uevent socket SO_RCVBUFFORCE option: %s", strerror(errno));
        close(*sock);
        return NULL;
    }

    if (setsockopt(*sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
        SLOGE("Unable to set uevent socket SO_PASSCRED option: %s", strerror(errno));
        close(*sock);
        return NULL;
    }

    if (bind(*sock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
        ALOGE("Unable to bind netlink socket: %s", strerror(errno));
        close(*sock);
        return NULL;
    }

    NetlinkHandler *handler = new NetlinkHandler(this, *sock, format);
    if (handler->start()) {
        ALOGE("Unable to start NetlinkHandler: %s", strerror(errno));
        close(*sock);
        return NULL;
    }

    return handler;
}

int NetlinkManager::start() {
    if ((mUeventHandler = setupSocket(&mUeventSock, NETLINK_KOBJECT_UEVENT,
         0xffffffff, NetlinkListener::NETLINK_FORMAT_ASCII)) == NULL) {
        return -1;
    }

    if ((mRouteHandler = setupSocket(&mRouteSock, NETLINK_ROUTE,
                                     RTMGRP_LINK |
                                     RTMGRP_IPV4_IFADDR |
                                     RTMGRP_IPV6_IFADDR,
         NetlinkListener::NETLINK_FORMAT_BINARY)) == NULL) {
        return -1;
    }

    if ((mQuotaHandler = setupSocket(&mQuotaSock, NETLINK_NFLOG,
        NFLOG_QUOTA_GROUP, NetlinkListener::NETLINK_FORMAT_BINARY)) == NULL) {
        ALOGE("Unable to open quota2 logging socket");
        // TODO: return -1 once the emulator gets a new kernel.
    }

    return 0;
}

NetlinkManager主要是向kernel注册三个用于接收UEvent事件的socket
1.NETLINK_KOBJECT_UEVENT
一般是/sys/class/net模块下的加载和卸载消息
2.RTMGRP_LINK、RTMGRP_IPV4_IFADDR、RTMGRP_IPV6_IFADDR
一般是网络链路接通或断开时的消息
3.NETLINK_NFLOG
一般和带宽控制有关
从上面源码中可以看到三个UEvent事件分别对应了mUeventHandler 、mRouteHandler 、mQuotaHandler,而这三个事件在Netd模块都声明为NetlinkHandler。

void NetlinkHandler::onEvent(NetlinkEvent *evt) {
    const char *subsys = evt->getSubsystem();
    if (!subsys) {
        ALOGW("No subsystem found in netlink event");
        return;
    }
    if (!strcmp(subsys, "net")) {
        int action = evt->getAction();
        const char *iface = evt->findParam("INTERFACE");

        if (action == evt->NlActionAdd) {
            notifyInterfaceAdded(iface);
        }
        ...
    }
}

NetlinkHandler收到的socket消息就是通过onEvent回调处理的。
例如:

void NetlinkHandler::notifyInterfaceAdded(const char *name) {
    char msg[255];
    snprintf(msg, sizeof(msg), "Iface added %s", name);

    mNm->getBroadcaster()->sendBroadcast(ResponseCode::InterfaceChange,
            msg, false);
}

这里间接调用了SocketListener发送一个消息。
源码位置:\system\core\libsysutils\src:

image.png
void SocketListener::init(const char *socketName, int socketFd, bool listen, bool useCmdNum) {
    mListen = listen;
    mSocketName = socketName;
    mSock = socketFd;
    mUseCmdNum = useCmdNum;
    pthread_mutex_init(&mClientsLock, NULL);
    mClients = new SocketClientCollection();
}

int SocketListener::startListener() {
    ...
    if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {
        SLOGE("pthread_create (%s)", strerror(errno));
        return -1;
    }
    return 0;
}

void *SocketListener::threadStart(void *obj) {
    SocketListener *me = reinterpret_cast<SocketListener *>(obj);

    me->runListener();
    pthread_exit(NULL);
    return NULL;
}

void SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) {
    pthread_mutex_lock(&mClientsLock);
    SocketClientCollection::iterator i;

    for (i = mClients->begin(); i != mClients->end(); ++i) {
        // broadcasts are unsolicited and should not include a cmd number
        if ((*i)->sendMsg(code, msg, addErrno, false)) {
            SLOGW("Error sending broadcast (%s)", strerror(errno));
        }
    }
    pthread_mutex_unlock(&mClientsLock);
}

...

每一个SockectListener都会单独创建一个线程用于接收socket消息。当Kernel发送UEvent消息后,就可以通过子类onDataAvailable函数回调回去处理。

void SocketListener::runListener() {

    SocketClientCollection *pendingList = new SocketClientCollection();

    while(1) {
        SocketClientCollection::iterator it;
        fd_set read_fds;
        int rc = 0;
        int max = -1;

        FD_ZERO(&read_fds);

        if (mListen) {
            max = mSock;
            FD_SET(mSock, &read_fds);
        }
        ...
        if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {
        ...
        }
        if (!onDataAvailable(c) && mListen) {
            ...
        }
}

比如NetlinkListener是SocketListener的派生,在接收到Kernel的Uevent消息后,先通过NetlinkEvent解析消息,然后通过onEvent接口回调处理,在NetlinkHandler中最终江通过Broadcaster转发出去。

NetlinkListener::NetlinkListener(int socket) :
                            SocketListener(socket, false) {
    mFormat = NETLINK_FORMAT_ASCII;
}
bool NetlinkListener::onDataAvailable(SocketClient *cli)
{
    int socket = cli->getSocket();
    ssize_t count;
    uid_t uid = -1;
    count = TEMP_FAILURE_RETRY(uevent_kernel_multicast_uid_recv(
                                       socket, mBuffer, sizeof(mBuffer), &uid));
    if (count < 0) {
        if (uid > 0)
            LOG_EVENT_INT(65537, uid);
        SLOGE("recvmsg failed (%s)", strerror(errno));
        return false;
    }

    NetlinkEvent *evt = new NetlinkEvent();
    if (!evt->decode(mBuffer, count, mFormat)) {
        SLOGE("Error decoding NetlinkEvent");
    } else {
        onEvent(evt);
    }

    delete evt;
    return true;
}

从SocketListener::sendBroadcast方法中可以看到,消息是通过FrameworkClient转发的:

FrameworkClient::FrameworkClient(int socket) {
    mSocket = socket;
    pthread_mutex_init(&mWriteMutex, NULL);
}

int FrameworkClient::sendMsg(const char *msg) {
    int ret;
    if (mSocket < 0) {
        errno = EHOSTUNREACH;
        return -1;
    }

    pthread_mutex_lock(&mWriteMutex);
    ret = TEMP_FAILURE_RETRY(write(mSocket, msg, strlen(msg) +1));
    if (ret < 0) {
        SLOGW("Unable to send msg '%s' (%s)", msg, strerror(errno));
    }
    pthread_mutex_unlock(&mWriteMutex);
    return 0;
}

int FrameworkClient::sendMsg(const char *msg, const char *data) {
    size_t bufflen = strlen(msg) + strlen(data) + 1;
    char *buffer = (char *) alloca(bufflen);
    if (!buffer) {
        errno = -ENOMEM;
        return -1;
    }
    snprintf(buffer, bufflen, "%s%s", msg, data);
    return sendMsg(buffer);
}

而在Android Framework层的NetworkManagementService实现中,可以看到通过NativeDaemonConnector对netd的sockect监听,当FrameworkClient转发消息到socket时,NativeDaemonConnector就会取出消息然后通过java层的Observer转发出去,进而实现底层消息的上报:

    private static final String NETD_SOCKET_NAME = "netd";
    public static NetworkManagementService create(Context context) throws InterruptedException {
        return create(context, NETD_SOCKET_NAME);
    }

    static NetworkManagementService create(Context context,
            String socket) throws InterruptedException {
        final NetworkManagementService service = new NetworkManagementService(context, socket);
        final CountDownLatch connectedSignal = service.mConnectedSignal;
        if (DBG) Slog.d(TAG, "Creating NetworkManagementService");
        service.mThread.start();
        if (DBG) Slog.d(TAG, "Awaiting socket connection");
        connectedSignal.await();
        if (DBG) Slog.d(TAG, "Connected");
        return service;
    }

    private NetworkManagementService(Context context, String socket) {
        mContext = context;

        if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
            return;
        }

        mConnector = new NativeDaemonConnector(
                new NetdCallbackReceiver(), socket, 10, NETD_TAG, 160);
        mThread = new Thread(mConnector, NETD_TAG);

        // Add ourself to the Watchdog monitors.
        Watchdog.getInstance().addMonitor(this);
    }

    private class NetdCallbackReceiver implements INativeDaemonConnectorCallbacks {
        ...
        @Override
        public boolean onEvent(int code, String raw, String[] cooked) {
             switch (code) {
             case NetdResponseCode.InterfaceChange:
                ...
             case NetdResponseCode.InterfaceClassActivity:
                    /*
                     * An network interface class state changed (active/idle)
                     * Format: "NNN IfaceClass <active/idle> <label>"
                     */
                    if (cooked.length < 4 || !cooked[1].equals("IfaceClass")) {
                        throw new IllegalStateException(
                                String.format("Invalid event from daemon (%s)", raw));
                    }
                    boolean isActive = cooked[2].equals("active");
                    notifyInterfaceClassActivity(cooked[3], isActive);
                    return true;
                ...
            }
        }
    }

    /**
     * Notify our observers of a change in the data activity state of the interface
     */
    private void notifyInterfaceClassActivity(String label, boolean active) {
        final int length = mObservers.beginBroadcast();
        for (int i = 0; i < length; i++) {
            try {
                mObservers.getBroadcastItem(i).interfaceClassDataActivityChanged(label, active);
            } catch (RemoteException e) {
            } catch (RuntimeException e) {
            }
        }
        mObservers.finishBroadcast();
    }
final class NativeDaemonConnector implements Runnable, Handler.Callback, Watchdog.Monitor {
    NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket,
            int responseQueueSize, String logTag, int maxLogSize) {
        mCallbacks = callbacks;
        mSocket = socket;
        mResponseQueue = new ResponseQueue(responseQueueSize);
        mSequenceNumber = new AtomicInteger(0);
        TAG = logTag != null ? logTag : "NativeDaemonConnector";
        mLocalLog = new LocalLog(maxLogSize);
    }

    @Override
    public void run() {
        mCallbackHandler = new Handler(FgThread.get().getLooper(), this);

        while (true) {
            try {
                listenToSocket();
            } catch (Exception e) {
                loge("Error in NativeDaemonConnector: " + e);
                SystemClock.sleep(5000);
            }
        }
    }

    private void listenToSocket() throws IOException {
        LocalSocket socket = null;
        try {
            socket = new LocalSocket();
            LocalSocketAddress address = determineSocketAddress();

            socket.connect(address);
            InputStream inputStream = socket.getInputStream();
            synchronized (mDaemonLock) {
                mOutputStream = socket.getOutputStream();
            }

            mCallbacks.onDaemonConnected();

            byte[] buffer = new byte[BUFFER_SIZE];
            int start = 0;

            while (true) {
              ...
              mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage(
                                        event.getCode(), event.getRawEvent()));
              ...
           }
    }

Android Framework层核心类ConnectivityService中,事件的广播:

 public ConnectivityService(Context context, INetworkManagementService netManager,
            INetworkStatsService statsService, INetworkPolicyManager policyManager,
            NetworkFactory netFactory) {
       ...
       try {
            mNetd.registerObserver(mTethering);
            mNetd.registerObserver(mDataActivityObserver);
            mNetd.registerObserver(mClat);
        } catch (RemoteException e) {
            loge("Error registering observer :" + e);
        }
  }

   private INetworkManagementEventObserver mDataActivityObserver = new BaseNetworkObserver() {
        @Override
        public void interfaceClassDataActivityChanged(String label, boolean active) {
            int deviceType = Integer.parseInt(label);
            sendDataActivityBroadcast(deviceType, active);
        }
    };

    private void sendDataActivityBroadcast(int deviceType, boolean active) {
        Intent intent = new Intent(ConnectivityManager.ACTION_DATA_ACTIVITY_CHANGE);
        intent.putExtra(ConnectivityManager.EXTRA_DEVICE_TYPE, deviceType);
        intent.putExtra(ConnectivityManager.EXTRA_IS_ACTIVE, active);
        final long ident = Binder.clearCallingIdentity();
        try {
            mContext.sendOrderedBroadcastAsUser(intent, UserHandle.ALL,
                    RECEIVE_DATA_ACTIVITY_CHANGE, null, null, 0, null, null);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    }
public class BaseNetworkObserver extends INetworkManagementEventObserver.Stub {
    @Override
    public void interfaceStatusChanged(String iface, boolean up) {
        // default no-op
    }

    @Override
    public void interfaceRemoved(String iface) {
        // default no-op
    }

    @Override
    public void addressUpdated(String address, String iface, int flags, int scope) {
        // default no-op
    }
    @Override
    public void addressRemoved(String address, String iface, int flags, int scope) {
        // default no-op
    }

    @Override
    public void interfaceLinkStateChanged(String iface, boolean up) {
        // default no-op
    }

    @Override
    public void interfaceAdded(String iface) {
        // default no-op
    }

    @Override
    public void interfaceClassDataActivityChanged(String label, boolean active) {
        // default no-op
    }

    @Override
    public void limitReached(String limitName, String iface) {
        // default no-op
    }
}

分析到这一步,已经可以看到sendOrderedBroadcastAsUser这个接口,同学你应该很开心了吧!其他如interfaceAdded、interfaceRemoved、interfaceStatusChanged、interfaceLinkStateChanged等接口的事件上报亦可以按以上思路在源码中找到对应的实现。

以下是参考了深入理解Andoriod卷画的NetLinkManager工作流程图:


NetLinkManager工作流程

2.CommandListener

主要作用是把从Framework层NetworkManageService接收的指令转交到对应的指令对象去处理。

CommandListener::CommandListener(UidMarkMap *map) :
                 FrameworkListener("netd", true) {
    registerCmd(new InterfaceCmd());
    registerCmd(new IpFwdCmd());
    registerCmd(new TetherCmd());
    registerCmd(new NatCmd());
    registerCmd(new ListTtysCmd());
    registerCmd(new PppdCmd());
    registerCmd(new SoftapCmd());
    registerCmd(new BandwidthControlCmd());
    registerCmd(new IdletimerControlCmd());
    registerCmd(new ResolverCmd());
    registerCmd(new FirewallCmd());
    registerCmd(new ClatdCmd());
    registerCmd(new SambaControlCmd());
    registerCmd(new TraceRouteControlCmd());
    registerCmd(new BridgeControlCmd());
    if (!sSecondaryTableCtrl)
        sSecondaryTableCtrl = new SecondaryTableController(map);
    if (!sTetherCtrl)
        sTetherCtrl = new TetherController();
    if (!sNatCtrl)
        sNatCtrl = new NatController(sSecondaryTableCtrl);
    if (!sPppCtrl)
        sPppCtrl = new PppController();
    if (!sSoftapCtrl)
        sSoftapCtrl = new SoftapController();
    if (!sBandwidthCtrl)
        sBandwidthCtrl = new BandwidthController();
    if (!sIdletimerCtrl)
        sIdletimerCtrl = new IdletimerController();
    if (!sResolverCtrl)
        sResolverCtrl = new ResolverController();
    if (!sFirewallCtrl)
        sFirewallCtrl = new FirewallController();
    if (!sInterfaceCtrl)
        sInterfaceCtrl = new InterfaceController();
    if (!sClatdCtrl)
        sClatdCtrl = new ClatdController();
    if (!sSambaCtrl)
        sSambaCtrl = new SambaController();
    if (!sTraceRouteCtrl)
        sTraceRouteCtrl = new TraceRouteController();
    if (!sBridgeCtrl)
        sBridgeCtrl = new BridgeController();
    // Create chains for children modules
    createChildChains(V4V6, "filter", "INPUT", FILTER_INPUT);
    createChildChains(V4V6, "filter", "FORWARD", FILTER_FORWARD);
    createChildChains(V4V6, "filter", "OUTPUT", FILTER_OUTPUT);
    createChildChains(V4V6, "raw", "PREROUTING", RAW_PREROUTING);
    createChildChains(V4V6, "mangle", "POSTROUTING", MANGLE_POSTROUTING);
    createChildChains(V4V6, "mangle", "OUTPUT", MANGLE_OUTPUT);
    createChildChains(V4, "nat", "PREROUTING", NAT_PREROUTING);
    createChildChains(V4, "nat", "POSTROUTING", NAT_POSTROUTING);

    // Let each module setup their child chains
    setupOemIptablesHook();
    sFirewallCtrl->setupIptablesHooks();

    /* Does DROPs in FORWARD by default */
    sNatCtrl->setupIptablesHooks();
    /*
     * Does REJECT in INPUT, OUTPUT. Does counting also.
     * No DROP/REJECT allowed later in netfilter-flow hook order.
     */
    sBandwidthCtrl->setupIptablesHooks();
    /*
     * Counts in nat: PREROUTING, POSTROUTING.
     * No DROP/REJECT allowed later in netfilter-flow hook order.
     */
    sIdletimerCtrl->setupIptablesHooks();

    sBandwidthCtrl->enableBandwidthControl(false);

    sSecondaryTableCtrl->setupIptablesHooks();
}

CommandListener是从FrameworkListener派生,在FrameworkListener内部有一个数组mCommands来存储注册到FrameworkListener中的命令处理对象。

FrameworkListener从SocketListener派生,在第一节分析SocketListener时就已经说过每一个SocketListener就是一个单独的线程用于接收sockect消息,当接收到sockect端消息时,就会回调onDataAvailable接口:

FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) :
                            SocketListener(socketName, true, withSeq) {
    init(socketName, withSeq);
}

void FrameworkListener::registerCmd(FrameworkCommand *cmd) {
    mCommands->push_back(cmd);
}

bool FrameworkListener::onDataAvailable(SocketClient *c) {
    ...
    dispatchCommand(c, buffer + offset);
    ...
}

void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {
    ...
        FrameworkCommand *c = *i;
        if (!strcmp(argv[0], c->getCommand())) {
            if (c->runCommand(cli, argc, argv)) {
                SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));
            }
            goto out;
        }
    ...
}

从CommandListener源码中可以看到类似InterfaceCmd、IpFwdCmd等和网络相关的Command类,这些类都是从NetdCommand派生的:

CommandListener::InterfaceCmd::InterfaceCmd() :
                 NetdCommand("interface") {
}

int CommandListener::InterfaceCmd::runCommand(SocketClient *cli,
                                                      int argc, char **argv) {
    if (argc < 2) {
        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
        return 0;
    }

    if (!strcmp(argv[1], "list")) {
    ...
}

CommandListener::IpFwdCmd::IpFwdCmd() :
                 NetdCommand("ipfwd") {
}

int CommandListener::IpFwdCmd::runCommand(SocketClient *cli,
                                                      int argc, char **argv) {
    int rc = 0;
    char value[PROPERTY_VALUE_MAX];

    if (argc < 2) {
        cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing argument", false);
        return 0;
    }

    if (!strcmp(argv[1], "status")) {
    ...
}
...

而NetdCommand又是从FrameworkCommand派生的:

NetdCommand::NetdCommand(const char *cmd) :
              FrameworkCommand(cmd)  {
}
class FrameworkCommand { 
private:
    const char *mCommand;

public:

    FrameworkCommand(const char *cmd);
    virtual ~FrameworkCommand() { }

    virtual int runCommand(SocketClient *c, int argc, char **argv) = 0;

    const char *getCommand() { return mCommand; }
};
#include <sysutils/FrameworkCommand.h>

FrameworkCommand::FrameworkCommand(const char *cmd) {
    mCommand = cmd;
}

int FrameworkCommand::runCommand(SocketClient *c, int argc, char **argv) {
    SLOGW("Command %s has no run handler!", getCommand());
    errno = ENOSYS;
    return -1;
}

因此,当Framework中有类似interface、ipfwd、softap等sockect指令时,在netd模块中就会通过sockect的select查选到(可以看之前对SockectListener的分析),进而回调到FrameworkListener的OnDataAvialable接口,由于是派生的NetdCommand类,进而调用对应的runCommand接口,实现对指令的特殊处理。


Command指令

例如通过Android的系统设置打开AP热点(打开AP在Framework层的流程和源码本文暂不做分析),SoftapController.cpp:

#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <string.h>

#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/wait.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include <linux/wireless.h>

#include <openssl/evp.h>
#include <openssl/sha.h>

#define LOG_TAG "SoftapController"
#include <cutils/log.h>
#include <netutils/ifc.h>
#include <private/android_filesystem_config.h>
#include "wifi.h"
#include "ResponseCode.h"

#include "SoftapController.h"
static const char HOSTAPD_CONF_FILE[]    = "/data/misc/wifi/hostapd.conf";
static const char HOSTAPD_BIN_FILE[]    = "/system/bin/hostapd";
SoftapController::SoftapController() {
    mPid = 0;
    mSock = socket(AF_INET, SOCK_DGRAM, 0);
    if (mSock < 0)
        ALOGE("Failed to open socket");
    memset(mIface, 0, sizeof(mIface));
}

SoftapController::~SoftapController() {
}

int SoftapController::startSoftap() {
    pid_t pid = 1;

    int wifi_device = wifi_get_device_id();
    if (WIFI_RALINK_RT3070 == wifi_device || WIFI_RALINK_RT5370 == wifi_device
      || WIFI_RALINK_RT5372 == wifi_device || WIFI_RALINK_RT5572 == wifi_device
      || WIFI_RALINK_MT7601U == wifi_device)
        return 0;
    if (mPid) {
        ALOGE("SoftAP is already running");
        return ResponseCode::SoftapStatusResult;
    }

    if ((pid = fork()) < 0) {
        ALOGE("fork failed (%s)", strerror(errno));
        return ResponseCode::ServiceStartFailed;
    }
    if (!pid) {
        ensure_entropy_file_exists();
        if (execl(HOSTAPD_BIN_FILE, HOSTAPD_BIN_FILE,
                  "-e", WIFI_ENTROPY_FILE,
                  HOSTAPD_CONF_FILE, (char *) NULL)) {
            ALOGE("execl failed (%s)", strerror(errno));
        }
        ALOGE("SoftAP failed to start");
        return ResponseCode::ServiceStartFailed;
    } else {
        mPid = pid;
        ALOGD("SoftAP started successfully");
        usleep(AP_BSS_START_DELAY);
    }
    return ResponseCode::SoftapStatusResult;
}

int SoftapController::stopSoftap() {

    int wifi_device = wifi_get_device_id();
    if (WIFI_RALINK_RT3070 == wifi_device || WIFI_RALINK_RT5370 == wifi_device
      || WIFI_RALINK_RT5372 == wifi_device || WIFI_RALINK_RT5572 == wifi_device
      || WIFI_RALINK_MT7601U == wifi_device) {
        struct ifreq ifr;
        int  s;
        /* configure WiFi interface down */
        memset(&ifr, 0, sizeof(struct ifreq));
        strcpy(ifr.ifr_name, mIface);

        if((s = socket(AF_INET, SOCK_DGRAM, 0)) >= 0) {
            if(ioctl(s, SIOCGIFFLAGS, &ifr) >= 0) {
                ifr.ifr_flags = (ifr.ifr_flags & (~IFF_UP));
                ioctl(s, SIOCSIFFLAGS, &ifr);
            }
            close(s);
        }
        usleep(200000);
        return 0;
    }

    if (mPid == 0) {
        ALOGE("SoftAP is not running");
        return ResponseCode::SoftapStatusResult;
    }

    ALOGD("Stopping the SoftAP service...");
    kill(mPid, SIGTERM);
    waitpid(mPid, NULL, 0);

    mPid = 0;
    ALOGD("SoftAP stopped successfully");
    usleep(AP_BSS_STOP_DELAY);
    return ResponseCode::SoftapStatusResult;
}

bool SoftapController::isSoftapStarted() {
    return (mPid != 0);
}

/* configure softap by sending private ioctls to driver directly */
int SoftapController::ap_config_with_iwpriv_cmd(int s, char *ifname, char **argv)
{
    char tBuf[4096];
    struct iwreq wrq;
    struct iw_priv_args *priv_ptr;
    int i, j;
    int cmd = 0, sub_cmd = 0;
    int device_id;
    char mBuf[256];
    int hidden_ssid = 0;

    device_id = wifi_get_device_id();

    /* get all private commands that driver supported */
    strncpy(wrq.ifr_name, ifname, sizeof(wrq.ifr_name));
    wrq.u.data.pointer = tBuf;
    wrq.u.data.length = sizeof(tBuf) / sizeof(struct iw_priv_args);
    wrq.u.data.flags = 0;
    if (ioctl(s, SIOCGIWPRIV, &wrq) < 0) {
        return -1;
    }

    /* if driver don't support 'set' command, return failure */
    priv_ptr = (struct iw_priv_args *)wrq.u.data.pointer;
    for(i = 0; i < wrq.u.data.length; i++) {
        if (strcmp(priv_ptr[i].name, "set") == 0) {
            cmd = priv_ptr[i].cmd;
            break;
        }
    }
    if (i == wrq.u.data.length) {
        return -1;
    }

    /* get the 'set' command's ID */
    if (cmd < SIOCDEVPRIVATE) {
        for(j = 0; j < i; j++) {
            if ((priv_ptr[j].set_args == priv_ptr[i].set_args)
                && (priv_ptr[j].get_args == priv_ptr[i].get_args)
                && (priv_ptr[j].name[0] == '\0'))
                break;
        }
        if (j == i) {
            return -1;
        }
        sub_cmd = cmd;
        cmd = priv_ptr[j].cmd;
    }

    /* configure AP, order should be as follow
     *   1. Channel
     *   2. AuthMode
     *   3. EncrypType
     * for WPAPSK/WPA2PSK:
     *   4.Hidden/Broadcast SSID
     *   5. SSID (must after AuthMode and before Password)
     *   6. Password
     */
    strncpy(wrq.ifr_name, ifname, sizeof(wrq.ifr_name));
    wrq.u.data.pointer = mBuf;
    wrq.u.data.flags = sub_cmd;

    /* configure Channel */
    sprintf(mBuf, "Channel=%s", argv[2]);
    wrq.u.data.length = strlen(mBuf) + 1;
    if(ioctl(s, cmd, &wrq) < 0)
        return -1;

    /* configure AuthMode */
    if(!strcmp(argv[3], "wpa-psk"))
        sprintf(mBuf, "AuthMode=WPAPSK");
    else if (!strcmp(argv[3], "wpa2-psk"))
        sprintf(mBuf, "AuthMode=WPA2PSK");
    else
        sprintf(mBuf, "AuthMode=OPEN");
    wrq.u.data.length = strlen(mBuf) + 1;
    if(ioctl(s, cmd, &wrq) < 0)
        return -1;

    /* configure EncrypType */
    if (!strcmp(argv[3], "wpa-psk"))
        sprintf(mBuf, "EncrypType=AES");
    else if (!strcmp(argv[3], "wpa2-psk"))
        sprintf(mBuf, "EncrypType=AES");
    else
        sprintf(mBuf, "EncrypType=NONE");
    wrq.u.data.length = strlen(mBuf) + 1;
    if(ioctl(s, cmd, &wrq) < 0)
        return -1;

    /* configure hide SSID */
    if (!strcmp(argv[1], "hidden"))
        hidden_ssid = 1;
    sprintf(mBuf, "HideSSID=%d", hidden_ssid);
    wrq.u.data.length = strlen(mBuf) + 1;
    if(ioctl(s, cmd, &wrq) < 0)
        return -1;

    /* configure SSID */
    sprintf(mBuf, "SSID=%s", argv[0]);
    wrq.u.data.length = strlen(mBuf) + 1;
    if(ioctl(s, cmd, &wrq) < 0)
        return -1;

    /* configure password of WPAPSK/WPA2PSK */
    if (strcmp(argv[3], "open")) {
        sprintf(mBuf, "WPAPSK=%s", argv[4]);
        wrq.u.data.length = strlen(mBuf) + 1;
        if(ioctl(s, cmd, &wrq) < 0)
            return -1;

        if (device_id == WIFI_RALINK_MT7601U) {
            /* for MT7601U, configure SSID again */
            sprintf(mBuf, "SSID=%s", argv[0]);
            wrq.u.data.length = strlen(mBuf) + 1;
            if(ioctl(s, cmd, &wrq) < 0)
                return -1;
        }
    }

    return 0;
}

/*
 * Arguments:
 *  argv[2] - wlan interface
 *  argv[3] - SSID
 *  argv[4] - Broadcast/Hidden
 *  argv[5] - Channel
 *  argv[6] - Security
 *  argv[7] - Key
 */
int SoftapController::setSoftap(int argc, char *argv[]) {
    char psk_str[2*SHA256_DIGEST_LENGTH+1];
    int ret = ResponseCode::SoftapStatusResult;
    int i = 0;
    int fd;
    int hidden = 0;
    int channel = AP_CHANNEL_DEFAULT;
    int wifi_device;
    char hw_mode;
    char ht40_capab[32];
    char *wbuf = NULL;
    char *fbuf = NULL;

    if (argc < 5) {
        ALOGE("Softap set is missing arguments. Please use:");
        ALOGE("softap <wlan iface> <SSID> <hidden/broadcast> <channel> <wpa2?-psk|open> <passphrase>");
        return ResponseCode::CommandSyntaxError;
    }
        wifi_device = wifi_get_device_id();

    if (!strcasecmp(argv[4], "hidden")) {
        if (WIFI_ATHEROS_QCA1021X == wifi_device || WIFI_ATHEROS_QCA1021G == wifi_device
            || WIFI_ATHEROS_AR9374 == wifi_device)
            hidden = 2;
        else
            hidden = 1;
    }

    if (argc >= 5) {
        channel = atoi(argv[5]);
        if (channel <= 0)
            channel = AP_CHANNEL_DEFAULT;
    }
    memset(ht40_capab, 0, sizeof(ht40_capab));
    if (channel >= 36) {
        int ht40plus[] = {36, 44, 52, 60, 100, 108, 116, 124,
                             132, 149, 157};
        int ht40minus[] = {40, 48, 56, 64, 104, 112, 120, 128,
                              136, 153, 161};

        hw_mode = 'a';

        for (i = 0; i < sizeof(ht40plus)/sizeof(ht40plus[0]); i++)
            if (channel == ht40plus[i]) {
                strcpy(ht40_capab, "[SHORT-GI-40][HT40+]");
                break;
            }

        for (i = 0; i < sizeof(ht40minus)/sizeof(ht40minus[0]); i++)
            if (channel == ht40minus[i]) {
                strcpy(ht40_capab, "[SHORT-GI-40][HT40-]");
                break;
            }
    } else {
        hw_mode = 'g';

        if (channel > 7)
            strcpy(ht40_capab, "[SHORT-GI-40][HT40-]");
        else
            strcpy(ht40_capab, "[SHORT-GI-40][HT40+]");
    }

    char *ssid, *iface;

    if (mSock < 0) {
        ALOGE("Softap set - failed to open socket");
        return -1;
    }

    strncpy(mIface, argv[2], sizeof(mIface));
    iface = argv[2];
    if (WIFI_RALINK_RT3070 == wifi_device || WIFI_RALINK_RT5370 == wifi_device
       || WIFI_RALINK_RT5372 == wifi_device || WIFI_RALINK_RT5572 == wifi_device
       || WIFI_RALINK_MT7601U == wifi_device) {
        struct ifreq ifr;
        int s;

        /* RT3070/5370/5372/MT7601U don't use hostapd, driver reads RT2870AP.dat
         * and configures WiFi to AP mode while intialize. After initialization
         * complete, configure WiFi interface up will startup AP. Then
         * reconfigure AP by private commands.
         */
        memset(&ifr, 0, sizeof(struct ifreq));
        strcpy(ifr.ifr_name, iface);
        if((s = socket(AF_INET, SOCK_DGRAM, 0)) >= 0) {
            if(ioctl(s, SIOCGIFFLAGS, &ifr) >= 0) {
                ifr.ifr_flags = (ifr.ifr_flags | IFF_UP);
                if (ioctl(s, SIOCSIFFLAGS, &ifr) >= 0) {
                    ret = ap_config_with_iwpriv_cmd(s, iface, argv + 3);
                }
            }
            close(s);
        }
        return 0;
    }

    if (argc < 4) {
        ALOGE("Softap set - missing arguments");
        return -1;
    }

    if (argc > 3) {
        ssid = argv[3];
    } else {
        ssid = (char *)"AndroidAP";
    }
    asprintf(&wbuf, "interface=%s\ndriver=nl80211\nctrl_interface="
            "/data/misc/wifi/hostapd\nssid=%s\nchannel=%d\nieee80211n=1\n"
            "hw_mode=%c\nht_capab=[SHORT-GI-20]%s\nignore_broadcast_ssid=%d\n",
            argv[2], argv[3], channel, hw_mode, ht40_capab, hidden);

    if (argc > 7) {
        if (!strcmp(argv[6], "wpa-psk")) {
            generatePsk(argv[3], argv[7], psk_str);
            asprintf(&fbuf, "%swpa=1\nwpa_pairwise=TKIP CCMP\nwpa_psk=%s\n", wbuf, psk_str);
        } else if (!strcmp(argv[6], "wpa2-psk")) {
            generatePsk(argv[3], argv[7], psk_str);
            asprintf(&fbuf, "%swpa=2\nrsn_pairwise=CCMP\nwpa_psk=%s\n", wbuf, psk_str);
        } else if (!strcmp(argv[6], "open")) {
            asprintf(&fbuf, "%s", wbuf);
        }
    } else if (argc > 6) {
        if (!strcmp(argv[6], "open")) {
            asprintf(&fbuf, "%s", wbuf);
        }
    } else {
        asprintf(&fbuf, "%s", wbuf);
    }
    fd = open(HOSTAPD_CONF_FILE, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW, 0660);
    if (fd < 0) {
        ALOGE("Cannot update \"%s\": %s", HOSTAPD_CONF_FILE, strerror(errno));
        free(wbuf);
        free(fbuf);
        return ResponseCode::OperationFailed;
    }
    if (write(fd, fbuf, strlen(fbuf)) < 0) {
        ALOGE("Cannot write to \"%s\": %s", HOSTAPD_CONF_FILE, strerror(errno));
        ret = ResponseCode::OperationFailed;
    }
    free(wbuf);
    free(fbuf);
    /* Note: apparently open can fail to set permissions correctly at times */
    if (fchmod(fd, 0660) < 0) {
        ALOGE("Error changing permissions of %s to 0660: %s",
                HOSTAPD_CONF_FILE, strerror(errno));
        close(fd);
        unlink(HOSTAPD_CONF_FILE);
        return ResponseCode::OperationFailed;
    }

    if (fchown(fd, AID_SYSTEM, AID_WIFI) < 0) {
        ALOGE("Error changing group ownership of %s to %d: %s",
                HOSTAPD_CONF_FILE, AID_WIFI, strerror(errno));
        close(fd);
        unlink(HOSTAPD_CONF_FILE);
        return ResponseCode::OperationFailed;
    }

    close(fd);
    return ret;
}

void SoftapController::generatePsk(char *ssid, char *passphrase, char *psk_str) {
    unsigned char psk[SHA256_DIGEST_LENGTH];
    int j;
    // Use the PKCS#5 PBKDF2 with 4096 iterations
    PKCS5_PBKDF2_HMAC_SHA1(passphrase, strlen(passphrase),
            reinterpret_cast<const unsigned char *>(ssid), strlen(ssid),
            4096, SHA256_DIGEST_LENGTH, psk);
    for (j=0; j < SHA256_DIGEST_LENGTH; j++) {
        sprintf(&psk_str[j*2], "%02x", psk[j]);
    }
}

首先可以看看SoftapController的startSoftap、stopSoftap、setSoftap接口,然后就可以联想到CommandListener源码中对Softap的处理,当接收到Framework层的set指令时调用的SoftapController的setSoftap,配置好AP参数后,Framework层再调用startap指令,就可以成功开启AP热点了。

int CommandListener::SoftapCmd::runCommand(SocketClient *cli,
                                        int argc, char **argv) {
    int rc = ResponseCode::SoftapStatusResult;
    int flag = 0;
    char *retbuf = NULL;

    if (sSoftapCtrl == NULL) {
      cli->sendMsg(ResponseCode::ServiceStartFailed, "SoftAP is not available", false);
      return -1;
    }
    if (argc < 2) {
        cli->sendMsg(ResponseCode::CommandSyntaxError,
                     "Missing argument in a SoftAP command", false);
        return 0;
    }
    if (!strcmp(argv[1], "startap")) {
        rc = sSoftapCtrl->startSoftap();
    } else if (!strcmp(argv[1], "stopap")) {
        rc = sSoftapCtrl->stopSoftap();
    } else if (!strcmp(argv[1], "fwreload")) {
        rc = sSoftapCtrl->fwReloadSoftap(argc, argv);
    } else if (!strcmp(argv[1], "status")) {
        asprintf(&retbuf, "Softap service %s running",
                 (sSoftapCtrl->isSoftapStarted() ? "is" : "is not"));
        cli->sendMsg(rc, retbuf, false);
        free(retbuf);
        return 0;
    } else if (!strcmp(argv[1], "set")) {
        rc = sSoftapCtrl->setSoftap(argc, argv);
    } else {
        cli->sendMsg(ResponseCode::CommandSyntaxError, "Unrecognized SoftAP command", false);
        return 0;
    }

    if (rc >= 400 && rc < 600)
      cli->sendMsg(rc, "SoftAP command has failed", false);
    else
      cli->sendMsg(rc, "Ok", false);

    return 0;
}

源码中在setSoftap时分为
1.使用ioctls接口配置驱动( RT3070/5370/5372/MT7601U don't use hostapd, driver reads RT2870AP.dat)
2.通过hostapd方式配置AP,即把AP配置写入/data/misc/wifi/hostapd.conf,在执行/system/bin/hostapd时读取配置
以下是参考了深入理解Andoriod卷画的CommandListener工作流程图:


CommandListener工作流程图

3.结束语

Netd进程本身的代码不多,但是涉及的面比较广,本篇文章先只介绍一个骨架,并没有深入骨髓,文章会继续更新,谢谢关注!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,434评论 25 707
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,571评论 18 399
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,594评论 18 139
  • 6)从此刻起: 我要用心去陪伴孩子, 而不是心不在焉的敷衍孩子。 因为我知道只有真正的陪伴才能让孩子感受到爱的温暖...
    一1一1一1一1一阅读 1,066评论 0 3
  • 一直以来控制和安全感的关系,不是很理解。在昨天忽然有了那么一些感悟,来说说我的感悟吧。 在婴幼儿时期的我们,完全是...
    阳光洒洒阅读 276评论 11 1