• 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发送一个消息。
    源码位置:systemcorelibsysutilssrc:

    img

    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工作流程图:

    img

    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接口,实现对指令的特殊处理。

    img

    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] == ''))
                    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
    driver=nl80211
    ctrl_interface="
                "/data/misc/wifi/hostapd
    ssid=%s
    channel=%d
    ieee80211n=1
    "
                "hw_mode=%c
    ht_capab=[SHORT-GI-20]%s
    ignore_broadcast_ssid=%d
    ",
                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
    wpa_pairwise=TKIP CCMP
    wpa_psk=%s
    ", wbuf, psk_str);
            } else if (!strcmp(argv[6], "wpa2-psk")) {
                generatePsk(argv[3], argv[7], psk_str);
                asprintf(&fbuf, "%swpa=2
    rsn_pairwise=CCMP
    wpa_psk=%s
    ", 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工作流程图:

    img

    CommandListener工作流程图

    3.结束语

    Netd进程本身的代码不多,但是涉及的面比较广。
    链接:https://www.jianshu.com/p/f752b2019c97

  • 相关阅读:
    第八天
    第六天 yum 磁盘分区与格式化
    第五天 权限掩码vim编辑器 软件包安装和管理
    第12组 Alpha冲刺 总结
    第12组 Alpha冲刺(6/6)
    第12组 Alpha冲刺(5/6)
    第12组 Alpha冲刺(4/6)
    第12组 Alpha冲刺(3/6)
    第12组 Alpha冲刺(2/6)
    第12组 Alpha冲刺(1/6)
  • 原文地址:https://www.cnblogs.com/lixuejian/p/15091225.html
Copyright © 2020-2023  润新知