NetlinkManager 管理 netd 中 NetlinkHandler 的初始化并启动监听和处理;

NetlinkHandler 处理和转发 Kernel 的相应事件;

SocketListener 监听 socket,启动监听并接收 socket 事件;

SocketClient 实际的消息处理者,他将 event 通过 socket 发送给 java 层进行处理;

NativeDaemonConnector java 层的 socket 通讯端,用于接收和下发事件;

NetworkManagementService Framework 层的网络相关核心服务;

① 在 netd 的 main 函数中通过 NetworkManager 的 Instance 方法创建 NetworkManager 对象,并且调用其 start 方法

int main() {...NetlinkManager *nm;...if (!(nm = NetlinkManager::Instance())) {ALOGE("Unable to create NetlinkManager");exit(1);};...if (nm->start()) {ALOGE("Unable to start NetlinkManager (%s)", strerror(errno));exit(1);}...
}

② 在 start 接口中创建了三个 socket 用于接收 Kernel 消息

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;
}

套接字的类型均为 PF_NETLINK,报文类型是 SOCK_DGRAM,协议分别为:

NETLINK_KOBJECT_UEVENT,NETLINK_ROUTE 和 NETLINK_NETFILTER;

③ 对每个新创建的 NETLINK_SOCKET 而言,又以其为参数创建了对应的 NetlinkHandler 对象,socket 的 fd 作为参数,用于 NetlinkHandler->NetlinkListener->SocketListener 的构造函数以及 init 函数初始化:

void SocketListener::init(const char *socketName, int socketFd, bool listen, bool useCmdNum) {mListen = listen;mSocketName = socketName;mSock = socketFd;mUseCmdNum = useCmdNum;pthread_mutex_init(&mClientsLock, nullptr);
}

这里面 listen 是 false,useCmdNum 也为 false,socketName 为 nullptr;

④ 继续对每个 NetlinkHandler 调用其 start 方法,其实最终调用了 SocketListener 的 startListener 方法,进而在其中以此 socket 的 fd 为参数创建了 SocketClient 对象,存入 mClient 容器中;

⑤ 创建一个新线程而以 SocketListener 类的 threadStart 方法为入口,调用了 runListener 方法,而其中首先将 fds 全部加入 vector 中再通过 poll 来阻塞监听,当有数据时将 active 的 SocketClient 对象加入到 pending 队列,释放互斥锁和引用,对 pending 的 list 进行遍历,通过 onDataAvailable 方法处理回调;

⑥ onDataAvailable 是 NetlinkListener 中的方法,其处理过程就是调用了 uevent_kernel_recv 方法,将数据接收到 mBuffer 中,创建一个 NetlinkEvent,将 mBuffer 中的数据用其 decode 进行分析并填充 NetlinkEvent 中的字段指定 action 信息,最终调用 NetlinkHandler 的 onEvent 方法进行 NetlinkEvent 的处理工作;

⑦ onEvent 方法进行消息类型的判断根据 action 匹配对应的 notifyXXX 方法;以添加接口为例,notifyInterfaceAdded 方法被调用,这里用到了 LOG_EVENT_FUNC 宏,实际上是调用了 gCtrls 中所有的 listeners 的 onInterfaceAdded 等方法,gCtrls 分析如下面第⑧步骤分析,listener 的注册见下面第⑨点分析;

⑧ 在 main 函数中执行 NetlinkManager 的 start 方法之前创建了 gCtls:

int main() {...NetlinkManager *nm = NetlinkManager::Instance();if (nm == nullptr) {ALOGE("Unable to create NetlinkManager");exit(1);};gCtls = new android::net::Controllers();gCtls->init();...
}

这里通过其内部的 EventReporter 对象的 getNetdEventListener 方法获得对应的 binder 服务端对象,可以对其中的方法进行调用,也就是上面讲到的 listeners 的 onInterfaceAdded 方法;

⑨ 同样 EventReporter::registerUnsolEventListener 被调用用来将 INetdUnsolicitedEventListener 对象 listener 注册到 netd 中,保存到 mUnsolListeners 中,以 NetworkManagementService 为例,其注册过程如下:

    static NetworkManagementService create(Context context, SystemServices services)throws InterruptedException {final NetworkManagementService service =new NetworkManagementService(context, services);if (DBG) Slog.d(TAG, "Creating NetworkManagementService");if (DBG) Slog.d(TAG, "Connecting native netd service");service.connectNativeNetdService();if (DBG) Slog.d(TAG, "Connected");return service;}private void connectNativeNetdService() {mNetdService = mServices.getNetd();try {mNetdService.registerUnsolicitedEventListener(mNetdUnsolicitedEventListener);if (DBG) Slog.d(TAG, "Register unsolicited event listener");} catch (RemoteException | ServiceSpecificException e) {Slog.e(TAG, "Failed to set Netd unsolicited event listener " + e);}}/* system/netd/server/binder/android/net/INetd.aidl *//**  * Register unsolicited event listener* Netd supports multiple unsolicited event listeners.*    * @param listener unsolicited event listener to register* @throws ServiceSpecificException in case of failure, with an error code indicating the*         cause of the failure.*/   void registerUnsolicitedEventListener(INetdUnsolicitedEventListener listener);/* system/netd/server/NetdNativeService.h */
/* system/netd/server/NetdNativeService.cpp */
binder::Status NetdNativeService::registerUnsolicitedEventListener(const android::sp<android::net::INetdUnsolicitedEventListener>& listener) {ENFORCE_NETWORK_STACK_PERMISSIONS();gCtls->eventReporter.registerUnsolEventListener(listener);return binder::Status::ok();
}/* EventReporter.cpp */
void EventReporter::registerUnsolEventListener(const android::sp<INetdUnsolicitedEventListener>& listener) {std::lock_guard lock(mUnsolicitedMutex);mUnsolListeners.insert(listener);// Create the death listener.class DeathRecipient : public android::IBinder::DeathRecipient {public:DeathRecipient(UnsolListeners* listeners,android::sp<INetdUnsolicitedEventListener> listener, std::mutex& unsolMutex): mMutex(unsolMutex), mUnsolListeners(listeners), mListener(std::move(listener)) {}~DeathRecipient() override = default;private:void binderDied(const android::wp<android::IBinder>& /* who */) override {std::lock_guard lock(mMutex);mUnsolListeners->erase(mListener);}std::mutex& mMutex;UnsolListeners* mUnsolListeners GUARDED_BY(mMutex);android::sp<INetdUnsolicitedEventListener> mListener;};android::sp<android::IBinder::DeathRecipient> deathRecipient =new DeathRecipient(&mUnsolListeners, listener, mUnsolicitedMutex);android::IInterface::asBinder(listener)->linkToDeath(deathRecipient);
}

这样的话 kernel 中的事件就可以通过 netd 上传回调到对应的 listener 了,比如 NetworkManagementService 服务类。WiFi 也同样注册了 listener,发生在 NetdWrapper 类中,首先定义了 NetdUnsolicitedEventListener 对象:

    private class NetdUnsolicitedEventListener extends INetdUnsolicitedEventListener.Stub {...@Overridepublic void onInterfaceChanged(String ifName, boolean up)throws RemoteException {mHandler.post(() -> notifyInterfaceStatusChanged(ifName, up));}@Overridepublic void onInterfaceLinkStateChanged(String ifName, boolean up)throws RemoteException {mHandler.post(() -> notifyInterfaceLinkStateChanged(ifName, up));}...@Overridepublic int getInterfaceVersion() {return INetdUnsolicitedEventListener.VERSION;}@Overridepublic String getInterfaceHash() {return INetdUnsolicitedEventListener.HASH;}}

在构造函数中完成了对应对象的注册:

/* frameworks/opt/net/wifi/service/java/com/android/server/wifi/util/NetdWrapper.java */public NetdWrapper(Context context, Handler handler) {mNetdService = INetd.Stub.asInterface((IBinder) context.getSystemService(Context.NETD_SERVICE));mNetdUnsolicitedEventListener = new NetdUnsolicitedEventListener();mHandler = handler;try {mNetdService.registerUnsolicitedEventListener(mNetdUnsolicitedEventListener);} catch (RemoteException | ServiceSpecificException e) {Log.e(TAG, "Failed to set Netd unsolicited event listener " + e);}}

最后需要说明的是 uevent_kernel_recv 函数处理实际的收包过程,它实现在 system/core/libcutils/uevent.cpp 文件中:

/*** Like recv(), but checks that messages actually originate from the kernel.*/
ssize_t uevent_kernel_multicast_recv(int socket, void* buffer, size_t length) {uid_t uid = -1;return uevent_kernel_multicast_uid_recv(socket, buffer, length, &uid);
}
/*** Like the above, but passes a uid_t in by pointer. In the event that this* fails due to a bad uid check, the uid_t will be set to the uid of the* socket's peer.** If this method rejects a netlink message from outside the kernel, it* returns -1, sets errno to EIO, and sets "user" to the UID associated with the* message. If the peer UID cannot be determined, "user" is set to -1."*/
ssize_t uevent_kernel_multicast_uid_recv(int socket, void* buffer, size_t length, uid_t* uid) {return uevent_kernel_recv(socket, buffer, length, true, uid);
}
ssize_t uevent_kernel_recv(int socket, void* buffer, size_t length, bool require_group, uid_t* uid) {struct iovec iov = {buffer, length};struct sockaddr_nl addr;char control[CMSG_SPACE(sizeof(struct ucred))];struct msghdr hdr = {&addr, sizeof(addr), &iov, 1, control, sizeof(control), 0,};struct ucred* cred;*uid = -1;ssize_t n = TEMP_FAILURE_RETRY(recvmsg(socket, &hdr, 0));if (n <= 0) {return n;}struct cmsghdr* cmsg = CMSG_FIRSTHDR(&hdr);if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {/* ignoring netlink message with no sender credentials */goto out;}cred = (struct ucred*)CMSG_DATA(cmsg);*uid = cred->uid;if (addr.nl_pid != 0) {/* ignore non-kernel */goto out;}if (require_group && addr.nl_groups == 0) {/* ignore unicast messages when requested */goto out;}return n;
out:/* clear residual potentially malicious data */bzero(buffer, length);errno = EIO;return -1;
}
int uevent_open_socket(int buf_sz, bool passcred) {struct sockaddr_nl addr;int on = passcred;int buf_sz_readback = 0;socklen_t optlen = sizeof(buf_sz_readback);int s;memset(&addr, 0, sizeof(addr));addr.nl_family = AF_NETLINK;addr.nl_pid = 0;addr.nl_groups = 0xffffffff;s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);if (s < 0) return -1;if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz, sizeof(buf_sz)) < 0 ||getsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz_readback, &optlen) < 0) {close(s);return -1;}/* Only if SO_RCVBUF was not effective, try SO_RCVBUFFORCE. Generally, we* want to avoid SO_RCVBUFFORCE, because it generates SELinux denials in* case we don't have CAP_NET_ADMIN. This is the case, for example, for* healthd. */if (buf_sz_readback < 2 * buf_sz) {if (setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &buf_sz, sizeof(buf_sz)) < 0) {close(s);return -1;}}setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) < 0) {close(s);return -1;}return s;
}

事实上其内部调用了 recvmsg 进行实际的报文接收工作,然后对报文做了简单的解析和处理。

Netd 中 NetworkManager 分析相关推荐

  1. 如何使用netwokx进行复杂网络的中心性分析?

    如何使用netwokx进行复杂网络的中心性分析? 这是本学期在大数据哲学与社会科学实验室做的第七次分享了. 第一次分享的是: 如何利用"wordcloud+jieba"制作中文词云 ...

  2. SQL Server 2005中的分析服务功能[转]

    XXXX(不知道为什么CnBlogs上的人这么抵制XXXX,呵呵--)上推出了"体验SQL Server 2005"活动,当然啦,一些关于SQL Server 2005的文章被翻译 ...

  3. 网络推广专员浅析网络推广中如何分析网站建设水平孰优孰劣?

    随着网站建设开发技术的成熟,网站建设从业人员也越来越多,很多站长并不是专业从事网站建设工作是半路出家的,像这种半路出家的群体并不在少数,他们在经过网站建设知识的积累后正式进入网络推广市场.那么对于这部 ...

  4. 科技论文中的分析与综合-如何写好科技论文之我见(七)

    科技论文中的分析与综合-----如何写好科技论文之我见(七) 闵应骅 分析与综合这两术语大家经常用.但是,真要说它们的定义,那可是哲学范围里的事.形式逻辑里面就有分析与综合.我在初中教几何的时候,就常 ...

  5. sonarqube中,分析maven聚合工程时,不必分析parent工程,只需分析下面的module子工程即可

    sonarqube中,分析maven聚合工程时,不必分析parent工程,只需分析下面的module子工程即可 cd ../../xxx-sms # mvn clean org.jacoco:jaco ...

  6. 中yeti不能加载_将 PQ 查询加载到 Excel 中进行分析的三种常用的方式

    点击上方蓝字 关注星标★不迷路 岁月本长,忙者自促 虽然大部分时候经过PQ清洗的数据都是加载到Excel工作表中,但是PQ中还有另外两种将数据返回Excel中进行分析的方法. 三种不同的数据加载方式: ...

  7. oracle 内存分析工具,IDE 中的分析工具

    IDE 中的分析工具 Oracle Solaris Studio IDE 提供的交互式图形分析工具可用于检查在 IDE 内部运行的项目的性能.分析工具使用 Oracle Solaris Studio ...

  8. Vue中computed分析

    Vue中computed分析 在Vue中computed是计算属性,其会根据所依赖的数据动态显示新的计算结果,虽然使用{{}}模板内的表达式非常便利,但是设计它们的初衷是用于简单运算的,在模板中放入太 ...

  9. 多头借贷数据在风控中如何分析及应用

    多头借贷数据在风控中如何分析及应用 金融风险管理中,对于一个借款人还款能力的评估十分重视.如果一个人的资产负债比过大,一旦发生资不抵债的现象,金融机构继续对其发放贷款发生违约的风险是极大的. 在体现借 ...

  10. 深度信念网络_【文章推荐】应用于油中溶解气体分析的深度信念网络与典型神经网络对比研究...

    文章推荐 应用于油中溶解气体分析的深度信念网络与典型神经网络对比研究 原文发表在<高压电器>2020年第9期. 请进<高压电器>网站(www.zgydq.com)下载全文. D ...

最新文章

  1. 去除lcd图片的摩尔纹_宝妈时尚产后有妊娠纹怎么办?教你这三招,轻松修复肚皮!...
  2. Leetcode 动态规划 Trapping Rain Water
  3. How to remove replication in SyteLine V2
  4. C#常用类 改配置文件
  5. 【CSP考前复习】关于考试时的注意事项
  6. Solr删除managedschema
  7. Flask 开发填坑
  8. python之地基(一)
  9. NPOI SetColumnHidden隐藏列不起作用的原因
  10. [软广]某数据领域在线教育机构
  11. 如何使用免费软件实现iPad当Windows电脑副屏的效果
  12. java gc roots_Java 虚拟机枚举 GC Roots 解析
  13. 几乎所有的RPG游戏(一种源自《龙与地下城》的游戏类型)在进入游戏时都会让用户自己来创建自己喜欢的角色。本次上机要求编写一个简化的创建游戏角色的程序。
  14. 使用HTMLTestRunner实现HTML测试报告
  15. Android中电池信息(Battery information)的取得
  16. C语言错误信息报告函数strerror、perror的使用
  17. Java 并发编程实战演练
  18. 安装ipython的命令是什么意思_ipython 命令
  19. 深圳seo优化分析:如何一个月把几十个关键词做到百度首页
  20. 计算机word图标不显示,word图标不显示怎么办 设置图标显示的具体方法

热门文章

  1. albers投影转WGS84坐标格式,读.shp.dbf文件,获取文件字符集工具类。
  2. 微信公众号80端口映射详解(一)
  3. QT绘制实时动态曲线——qcustomplot使用(一)
  4. mysqldump --set-gtid-purged=OFF
  5. 为什么闹钟设置了却不响_为什么苹果手机闹钟设置了却不响
  6. java查看eth转账状态_eth交易记录input解析
  7. word涂改涂掉图片_【最新】干部档案涂改检讨书-word范文 (20页)
  8. C#将图片转为二进制流
  9. word 技巧 - 删除指定页页眉
  10. 万兆交换机用什么网线_千兆网线和万兆网线有什么区别