Android WIFI 模块解析(2)
书接上文,这一章接着分析wifi
模块的Hal
层的调用逻辑.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56public String setupInterfaceForClientMode(boolean lowPrioritySta, @NonNull InterfaceCallback interfaceCallback) { synchronized (mLock) { //启动hal层. if (!startHal()) { Log.e(TAG, "Failed to start Hal"); mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToHal(); return null; } //启动wpa_supplicant if (!startSupplicant()) { Log.e(TAG, "Failed to start supplicant"); mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToSupplicant(); return null; } Iface iface = mIfaceMgr.allocateIface(Iface.IFACE_TYPE_STA); if (iface == null) { Log.e(TAG, "Failed to allocate new STA iface"); return null; } iface.externalListener = interfaceCallback; iface.name = createStaIface(iface, lowPrioritySta); if (TextUtils.isEmpty(iface.name)) { Log.e(TAG, "Failed to create STA iface in vendor HAL"); mIfaceMgr.removeIface(iface.id); mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToHal(); return null; } if (mWificondControl.setupInterfaceForClientMode(iface.name) == null) { Log.e(TAG, "Failed to setup iface in wificond on " + iface); teardownInterface(iface.name); mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToWificond(); return null; } if (!mSupplicantStaIfaceHal.setupIface(iface.name)) { Log.e(TAG, "Failed to setup iface in supplicant on " + iface); teardownInterface(iface.name); mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToSupplicant(); return null; } iface.networkObserver = new NetworkObserverInternal(iface.id); if (!registerNetworkObserver(iface.networkObserver)) { Log.e(TAG, "Failed to register network observer on " + iface); teardownInterface(iface.name); return null; } mWifiMonitor.startMonitoring(iface.name); // Just to avoid any race conditions with interface state change callbacks, // update the interface state before we exit. onInterfaceStateChanged(iface, isInterfaceUp(iface.name)); initializeNwParamsForClientInterface(iface.name); Log.i(TAG, "Successfully setup " + iface); return iface.name; } }
这是上一章中贴出的最后一个函数,本章我们重点分析startHal()
函数和startSupplicant()
函数.
startHal
调用过程
我们先追逐一下其代码过程
startHal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19/** Helper method invoked to start supplicant if there were no ifaces */ private boolean startHal() { synchronized (mLock) { if (!mIfaceMgr.hasAnyIface()) { //判断其是否当前的设备供应商Hal层是否可用 if (mWifiVendorHal.isVendorHalSupported()) { //启动设备Hal if (!mWifiVendorHal.startVendorHal()) { Log.e(TAG, "Failed to start vendor HAL"); return false; } } else { Log.i(TAG, "Vendor Hal not supported, ignoring start."); } } return true; } }
WifiVendorHal
这个类注释是厂商hal层对HIDL
的支持,HIDL
的发音hide-l
.后文专门解释一下这个东西
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24/** * Vendor HAL via HIDL */ public class WifiVendorHal { ... /** * Bring up the HIDL Vendor HAL. * @return true on success, false otherwise. */ public boolean startVendorHal() { synchronized (sLock) { // 在这里启动 if (!mHalDeviceManager.start()) { mLog.err("Failed to start vendor HAL").flush(); return false; } mLog.info("Vendor Hal started successfully").flush(); return true; } } ... }
HalDeviceManager
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65/** * Handles device management through the HAL (HIDL) interface. */ public class HalDeviceManager { /** * Attempts to start Wi-Fi (using HIDL). Returns the success (true) or failure (false) or * the start operation. Will also dispatch any registered ManagerStatusCallback.onStart() on * success. * * Note: direct call to HIDL. */ public boolean start() { return startWifi(); } private boolean startWifi() { if (VDBG) Log.d(TAG, "startWifi"); synchronized (mLock) { try { if (mWifi == null) { Log.w(TAG, "startWifi called but mWifi is null!?"); return false; } else { int triedCount = 0; while (triedCount <= START_HAL_RETRY_TIMES) { //在这里 WifiStatus status = mWifi.start(); if (status.code == WifiStatusCode.SUCCESS) { initIWifiChipDebugListeners(); managerStatusListenerDispatch(); if (triedCount != 0) { Log.d(TAG, "start IWifi succeeded after trying " + triedCount + " times"); } return true; } else if (status.code == WifiStatusCode.ERROR_NOT_AVAILABLE) { // Should retry. Hal might still be stopping. Log.e(TAG, "Cannot start IWifi: " + statusString(status) + ", Retrying..."); try { Thread.sleep(START_HAL_RETRY_INTERVAL_MS); } catch (InterruptedException ignore) { // no-op } triedCount++; } else { // Should not retry on other failures. Log.e(TAG, "Cannot start IWifi: " + statusString(status)); return false; } } Log.e(TAG, "Cannot start IWifi after trying " + triedCount + " times"); return false; } } catch (RemoteException e) { Log.e(TAG, "startWifi exception: " + e); return false; } } } }
在这里我们看到mWifi
这个对象去调用了start()
函数通过hal
层去启动与内核通信.那么mWifi
这个对象在何时创建.
1
2
3
4
5
6
7
8
9
10
11
12
13
14import android.hardware.wifi.V1_0.IWifi; /** * Wrapper function to access the HIDL services. Created to be mockable in unit-tests. */ protected IWifi getWifiServiceMockable() { try { return IWifi.getService(); } catch (RemoteException e) { Log.e(TAG, "Exception getting IWifi service: " + e); return null; } }
我们看下IWifi到底是何物
IWifi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76package android.hardware.wifi@1.0; import IWifiChip; import IWifiEventCallback; /** * This is the root of the HAL module and is the interface returned when * loading an implementation of the Wi-Fi HAL. There must be at most one * module loaded in the system. */ interface IWifi { /** * Requests notifications of significant events for the HAL. Multiple calls to * this must register multiple callbacks each of which must receive all * events. |IWifiEventCallback| object registration must be independent of the * state of the rest of the HAL and must persist though stops/starts. These * objects must be deleted when the corresponding client process is dead. * * @param callback An instance of the |IWifiEventCallback| HIDL interface * object. * @return status WifiStatus of the operation. * Possible status codes: * |WifiStatusCode.SUCCESS|, * |WifiStatusCode.UNKNOWN| */ @entry @callflow(next={"*"}) registerEventCallback(IWifiEventCallback callback) generates (WifiStatus status); /** * Get the current state of the HAL. * * @return started true if started, false otherwise. */ isStarted() generates (bool started); /** * Perform any setup that is required to make use of the module. If the module * is already started then this must be a noop. * Must trigger |IWifiEventCallback.onStart| on success. * * @return status WifiStatus of the operation. * Possible status codes: * |WifiStatusCode.SUCCESS|, * |WifiStatusCode.NOT_AVAILABLE|, * |WifiStatusCode.UNKNOWN| */ @entry @callflow(next={"registerEventCallback", "start", "stop", "getChip"}) start() generates (WifiStatus status); /** * Tear down any state, ongoing commands, etc. If the module is already * stopped then this must be a noop. If the HAL is already stopped or it * succeeds then onStop must be called. After calling this all IWifiChip * objects will be considered invalid. * Must trigger |IWifiEventCallback.onStop| on success. * Must trigger |IWifiEventCallback.onFailure| on failure. * * Calling stop then start is a valid way of resetting state in the HAL, * driver, firmware. * * @return status WifiStatus of the operation. * Possible status codes: * |WifiStatusCode.SUCCESS|, * |WifiStatusCode.NOT_STARTED|, * |WifiStatusCode.UNKNOWN| */ @exit @callflow(next={"registerEventCallback", "start", "stop"}) stop() generates (WifiStatus status); ... };
WIFI HAL 总结
IWifi.hal
文件位于hardware/interfaces/wifi/1.0/
,这个取决于厂商使用的版本.前文在HalDeviceManager
使用IWifi.getService();
这样的方式来获取服务,那么我们就会很疑惑,服务注册在何处,代码在何方.
问题的关键呢就在这个hal
文件中,它通过BP文件会生成Java
和C++
的模板代码,在bp文件中有不同的配置项配置它的生成代码.也可以自己通过命令去生成代码.
hidl-gen
工具路径在代码路径下out/host/linux-x86/bin/
路径下.可以通过hidl-gen -help
命令查看对于参数.但是这个东西在android 10
有被google
抛弃,改用了AIDL
方式.所以对这块就没有更深入的探究下去.
关于HIDL的官方介绍
简单的贴一下编译后的C++
的注册代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22int main(int /*argc*/, char** argv) { android::base::InitLogging( argv, android::base::LogdLogger(android::base::SYSTEM)); LOG(INFO) << "Wifi Hal is booting up..."; configureRpcThreadpool(1, true /* callerWillJoin */); // Setup hwbinder service 这个是1_2版本的注册代码,不是上文中的1_0的版本,但是注册的地吗几乎是一致的,作为参考查看 android::sp<android::hardware::wifi::V1_2::IWifi> service = new android::hardware::wifi::V1_2::implementation::Wifi( std::make_shared<WifiLegacyHal>(), std::make_shared<WifiModeController>(), std::make_shared<WifiFeatureFlags>()); CHECK_EQ(service->registerAsService(), android::NO_ERROR) << "Failed to register wifi HAL"; joinRpcThreadpool(); LOG(INFO) << "Wifi Hal is terminating..."; return 0; }
在这一块的参考资料确实有点少,理解也不够深入,举个例子
1
2
3
4
5
6
7
8
9
10
11
12
13
14/** * Perform any setup that is required to make use of the module. If the module * is already started then this must be a noop. * Must trigger |IWifiEventCallback.onStart| on success. * * @return status WifiStatus of the operation. * Possible status codes: * |WifiStatusCode.SUCCESS|, * |WifiStatusCode.NOT_AVAILABLE|, * |WifiStatusCode.UNKNOWN| */ android.hardware.wifi.V1_0.WifiStatus start() throws android.os.RemoteException;
上面是tart()
函数生成的JAvA
代码,下文是生成的C++的代码,没有搞清楚在C++
代码start
函数中的 _hidl_cb
参数从和而来,这个参数在后面的实际调用中也有用到.感兴趣的朋友,可以自己在深入的看下去,如果有答案了,有条件的话也告诉我一下.
1
2
3
4
5
6
7
8
9
10
11
12
13
14/** * Perform any setup that is required to make use of the module. If the module * is already started then this must be a noop. * Must trigger |IWifiEventCallback.onStart| on success. * * @return status WifiStatus of the operation. * Possible status codes: * |WifiStatusCode.SUCCESS|, * |WifiStatusCode.NOT_AVAILABLE|, * |WifiStatusCode.UNKNOWN| */ virtual ::android::hardware::Return<void> start(start_cb _hidl_cb) = 0;
startSupplicant
在成功的通过HAL层
代码启动wifi
设备之后,代码就可以去调用了startSupplicant()
函数,那么这个东西是干什么的,HAL层
对外提供了start
,stop
,getChipIds
,和注册回调函数等方案,其他并未提供,也就是说只有硬件相关的型号,和启动,暂停等关键函数,关于wifi
的链接维护等函数并未提供,startSupplicant
启动的就是wpa_sulicant
.
wpa_sulicant
wpa_sulicant
是一个开源项目,被google
经过修改加入到Android
系统当中.主要支持WPA,EAP,无线网卡和驱动.有兴趣致力于研究wifi
相关的朋友可以研究一下这个东西,本文浅尝辄止.
wpa_sulicant官网
一个很不错的讲解网站
调用过程
startSupplicant
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41/** Helper method invoked to start supplicant if there were no STA ifaces */ private boolean startSupplicant() { synchronized (mLock) { if (!mIfaceMgr.hasAnyStaIface()) { //这里 if (!mWificondControl.enableSupplicant()) { Log.e(TAG, "Failed to enable supplicant"); return false; } if (!waitForSupplicantConnection()) { Log.e(TAG, "Failed to connect to supplicant"); return false; } if (!mSupplicantStaIfaceHal.registerDeathHandler( new SupplicantDeathHandlerInternal())) { Log.e(TAG, "Failed to register supplicant death handler"); return false; } } return true; } } /** * Enable wpa_supplicant via wificond. * @return Returns true on success. */ public boolean enableSupplicant() { if (!retrieveWificondAndRegisterForDeath()) { return false; } try { //这里 return mWificond.enableSupplicant(); } catch (RemoteException e) { Log.e(TAG, "Failed to enable supplicant due to remote exception"); } return false; }
可以看到最终启动wpa_supplicant
使用一个wificond
的一个对象.我们看下它是怎么获取的.
WifiInjector
1
2
3
4
5
6
7
8
9private static final String WIFICOND_SERVICE_NAME = "wificond"; public IWificond makeWificond() { // We depend on being able to refresh our binder in WifiStateMachine, so don't cache it. IBinder binder = ServiceManager.getService(WIFICOND_SERVICE_NAME); return IWificond.Stub.asInterface(binder); }
也是通过binder
的形式进行通信,那么wificond
服务是何时注册到ServiceManager
中的呢.接着往下看
wificond
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23include $(CLEAR_VARS) LOCAL_MODULE := wificond LOCAL_CPPFLAGS := $(wificond_cpp_flags) LOCAL_INIT_RC := wificond.rc LOCAL_C_INCLUDES := $(wificond_includes) LOCAL_SRC_FILES := main.cpp LOCAL_SHARED_LIBRARIES := android.hardware.wifi.offload@1.0 libbinder libbase libcutils libhidlbase libhidltransport libminijail libutils libwifi-system libwifi-system-iface LOCAL_STATIC_LIBRARIES := libwificond include $(BUILD_EXECUTABLE)
这个是system/connectivity/wificond/android.mk
中的一段,大意就是编译一个Natvie C的可执行文件,module
为wificond
.
在对应的main.cpp
我们找到了注册服务的代码
1
2
3
4
5
6
7
8
9
10int main(int argc, char** argv) { ... RegisterServiceOrCrash(server.get()); event_dispatcher->Poll(); LOG(INFO) << "wificond is about to exit"; return 0; }
1
2
3
4
5
6
7
8
9void RegisterServiceOrCrash(const android::sp<android::IBinder>& service) { android::sp<android::IServiceManager> sm = android::defaultServiceManager(); CHECK_EQ(sm != NULL, true) << "Could not obtain IServiceManager"; //这里addService到ServiceManager中 CHECK_EQ(sm->addService(android::String16(kServiceName), service), android::NO_ERROR); }
那么它是在什么时候执行这个main.cpp
呢,跟同事讨论这个问题时,猜测
- 在开机流程中有一段代码是会执行所有服务的
main()
函数.(未经确认,我还没有仔细的梳理过开机流程).
不过我在init.zygote
文件找到这样一段脚本文件
1
2
3
4
5
6
7onrestart restart audioserver onrestart restart cameraserver onrestart restart media onrestart restart netd onrestart restart wificond
如果和推测一致呢就是在开机流程中已经执行了,在这里用restart
确保其一定被启动.负责无法理解要用 restart
去启动.好,解释了服务在何时注册,何时启动之后,我们接着往下看他是怎么启动wpa_supplicant
.
1
2
3
4
5
6
7Status Server::enableSupplicant(bool* success) { //这里,接着寻找 supplicant_manager_ *success = supplicant_manager_->StartSupplicant(); return Status::ok(); }
supplicant_manager
- 代码路径
frameworks/opt/net/wifi/libwifi_system/supplicant_manager.cpp
在这里呢就简单的分析一下它的启动代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68namespace { const char kSupplicantInitProperty[] = "init.svc.wpa_supplicant"; const char kSupplicantServiceName[] = "wpa_supplicant"; } // namespace bool SupplicantManager::StartSupplicant() { char supp_status[PROPERTY_VALUE_MAX] = {''}; int count = 200; /* wait at most 20 seconds for completion */ const prop_info* pi; unsigned serial = 0; /* Check whether already running */ //获取property属性 赋值给 supp_status ,字符串对比判断其是否已经启动,如果启动就 //return if (property_get(kSupplicantInitProperty, supp_status, NULL) && strcmp(supp_status, "running") == 0) { return true; } /* * Get a reference to the status property, so we can distinguish * the case where it goes stopped => running => stopped (i.e., * it start up, but fails right away) from the case in which * it starts in the stopped state and never manages to start * running at all. */ //找到属性值索引 pi = __system_property_find(kSupplicantInitProperty); if (pi != NULL) { //拿到其序号 serial = __system_property_serial(pi); } //启动 服务 property_set("ctl.start", kSupplicantServiceName); //让出自己的CPU控制权,将自己排到CPU序列队尾 sched_yield(); while (count-- > 0) { if (pi == NULL) { //空的话就重新获取获取一下索引 pi = __system_property_find(kSupplicantInitProperty); } if (pi != NULL) { /* * property serial updated means that init process is scheduled * after we sched_yield, further property status checking is based on this */ //用现在的序号和之前的序号做对比 if (__system_property_serial(pi) != serial) { //有改变就取出 __system_property_read(pi, NULL, supp_status); //对比字符串,如果已经在运行就 return if (strcmp(supp_status, "running") == 0) { return true; } else if (strcmp(supp_status, "stopped") == 0) { return false; } } } usleep(100000); } return false; }
理解上面的代码主要需要了解关于android
系统中property
相关的知识.例如ctl.start
是启动服务等等. 这个百度一下就有很多相关的文章,就不贴链接了.
总结
接下来就不去写关于wifi
链接和扫描等相关内容了,其代码量之大,平常需要改动的需求也是非常少的,有兴趣了解的根据梳理的流程自行查看.接下来我们画一个图,来表示整个流程中我们重点关注的模块,和其主要功能.
闲聊一些
-
Java层主要优化方向可能放在开机流程,不过提前启动应该收效甚微。
-
状态机优化,这个从Google不同版本的源代码就可以看出,Google自己在新的版本中也在不断的优化关于状态机的模块。 理解上层的逻辑代码也必须了解状态机。
-
hal层主要控制了硬件设备的启动和暂停。
-
wpa_supplicant,关注这个模块应该是深入了解和优化,也是一个开源项目。(留待以后深入研究。)
-
后续的计划呢,可能重心要放在优化方向。关于binder的文章还要再推迟,不知道今年能不能完成了。
文末
要想成为架构师,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。
如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。
一、架构师筑基必备技能
1、深入理解Java泛型
2、注解深入浅出
3、并发编程
4、数据传输与序列化
5、Java虚拟机原理
6、高效IO
……
二、Android百大框架源码解析
1.Retrofit 2.0源码解析
2.Okhttp3源码解析
3.ButterKnife源码解析
4.MPAndroidChart 源码解析
5.Glide源码解析
6.Leakcanary 源码解析
7.Universal-lmage-Loader源码解析
8.EventBus 3.0源码解析
9.zxing源码分析
10.Picasso源码解析
11.LottieAndroid使用详解及源码解析
12.Fresco 源码分析——图片加载流程
三、Android性能优化实战解析
- 腾讯Bugly:对字符串匹配算法的一点理解
- 爱奇艺:安卓APP崩溃捕获方案——xCrash
- 字节跳动:深入理解Gradle框架之一:Plugin, Extension, buildSrc
- 百度APP技术:Android H5首屏优化实践
- 支付宝客户端架构解析:Android 客户端启动速度优化之「垃圾回收」
- 携程:从智行 Android 项目看组件化架构实践
- 网易新闻构建优化:如何让你的构建速度“势如闪电”?
- …
四、高级kotlin强化实战
1、Kotlin入门教程
2、Kotlin 实战避坑指南
3、项目实战《Kotlin Jetpack 实战》
-
从一个膜拜大神的 Demo 开始
-
Kotlin 写 Gradle 脚本是一种什么体验?
-
Kotlin 编程的三重境界
-
Kotlin 高阶函数
-
Kotlin 泛型
-
Kotlin 扩展
-
Kotlin 委托
-
协程“不为人知”的调试技巧
-
图解协程:suspend
五、Android高级UI开源框架进阶解密
1.SmartRefreshLayout的使用
2.Android之PullToRefresh控件源码解析
3.Android-PullToRefresh下拉刷新库基本用法
4.LoadSir-高效易用的加载反馈页管理框架
5.Android通用LoadingView加载框架详解
6.MPAndroidChart实现LineChart(折线图)
7.hellocharts-android使用指南
8.SmartTable使用指南
9.开源项目android-uitableview介绍
10.ExcelPanel 使用指南
11.Android开源项目SlidingMenu深切解析
12.MaterialDrawer使用指南
六、NDK模块开发
1、NDK 模块开发
2、JNI 模块
3、Native 开发工具
4、Linux 编程
5、底层图片处理
6、音视频开发
7、机器学习
七、Flutter技术进阶
1、Flutter跨平台开发概述
2、Windows中Flutter开发环境搭建
3、编写你的第一个Flutter APP
4、Flutter开发环境搭建和调试
5、Dart语法篇之基础语法(一)
6、Dart语法篇之集合的使用与源码解析(二)
7、Dart语法篇之集合操作符函数与源码分析(三)
…
八、微信小程序开发
1、小程序概述及入门
2、小程序UI开发
3、API操作
4、购物商场项目实战……
全套视频资料:
一、面试合集
二、源码解析合集
三、开源框架合集
欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证微信卡片免费领取【保证100%免费】↓↓↓
最后
以上就是儒雅乌龟最近收集整理的关于Android WIFI 模块解析(2)Android WIFI 模块解析(2)的全部内容,更多相关Android内容请搜索靠谱客的其他文章。
发表评论 取消回复