Wifi流程机制分析:WiFi的启动
现如今无论是物联网时代的大发展还是5G的快速到来,WiFi因为其不可替代的重要性在这个时代中成了不可或缺的角色,现在去谈光联网这些还只是概念性的机制可能还为时尚早,所以在这些流行性时代前沿技术来临前,好好了解下WiFi的流程机制就显得尤为重要。考虑到网络上如今对于WiFi的讲解的文章过少,或者已经被时代抛弃了,所以秉着学习至上的原则,如果你想和我一起学习和了解WiFi机制,这系列文章或许就可能帮的到你。
注意本文是基于Android SDK28源码对WiFi整个流程机制进行分析,当然除了源码也会有其他方式讲解,且本文是以Android系统为整体分析流程。因为涉及到WiFi整体,篇幅过长,所以会就内容进行分章讲解。
WiFi简述
众所周知,WiFi是一种将有线信号转为无线电波信号的无线保真技术,是基于IEEE 802.11标准的一种无线技术,在我们现代生活中可谓举足轻重,但在Android开发中,我们常常通过WiFi的ScanResult类来进行WiFi的开发,下面简单介绍一下该类的一些重要成员,以便我们更好的描述和开发WiFi。
在1999年IEEE官方定义802.11标准的时候,IEEE选择并认定了CSIRO发明的无线网技术是世界上最好的无线网技术,因此CSIRO的无线网技术标准,就成为了2010年Wi-Fi的核心技术标准。
成员名 | 类型 | 描述 |
---|---|---|
BSSID | String | 访问点的MAC地址,可以做wifi的唯一标识 |
SSID | String | 访问点的网络名称,有可能会重名 |
capabilities | String | 描述了接入点所支持的身份验证、密钥管理和加密方案。可根据该字段判断wifi是否已进行加密 |
level | int | 检测到的信号强度,单位是dBm,也称作RSSI,常以负数存在 |
frenquency | int | 客户端与接入点通信的信道频率,单位是MHz |
WifiManager的获取
本篇文章将从wifi的启动前类的创建,实例获取,包括启动流程等方面对WiFi进行一系列分析。
首先,如果我们想要启动WiFi就不得不借助WifiManager这个类来进行,WifiManager通过Binder等机制和framework层及驱动层进行通信回调,可借助它来实现很多我们想要的基本功能,包括WiFi的启动,连接(隐藏方法),扫描,WiFi列表的获取等等都可以通过该类进行调用。那么在进行器内部函数调用前,因为要建立IBinder,所以我们需要先通过创建Service的方式拿到WiFiManager的实例类。
调用方式如下:
WifiManager wifiManager=(WifiManager)context.getSystemService(Context.WIFI_SERVICE);
调用流程简述如下:
为了不让各位有很多疑惑,下面我补充一下这样为什么就能获取到WiFiManager实例的过程,和WiFi启动的关联性不大,其主要目的是创建和WiFi底层通信的Service。
getSystemService #Context & #ContextImpl
首先自然是通过context执行getSystemService方法,该方法进行外包装返回内部通过传进去的 Service 。
//获取Service抽象方法
//#Context
public abstract @Nullable Object getSystemService(@ServiceName @NonNull String name);
//#ContextImpl
//通过SystemServiceRegistry进行获取系统服务
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
getSystemService #SystemServiceRegistry
SystemServiceRegistry是负责注册和获取系统服务的一个类,我们可以通过该类进行系统服务的获取,和注册(私有方法)。当然很多默认的系统服务已经在该类里注册好了。并且传入ConnectivityThread里的Loop单例以做循环执行。最后返回根据这些传参得到的一个WiFiManager实例,因为IWifiManager是系统framework隐藏了的一个接口类,所以我们不能在外面创建,需要借用SystemServiceRegistry进行注册和获取,获取的话在getSystemService(ContextImpl ctx, String name)方法里就直接可以通过SYSTEM_SERVICE_FETCHERS(一个HashMap)来获取得到。
//通过service name获取实例对象
public static Object getSystemService(ContextImpl ctx, String name) {
ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);
return fetcher != null ? fetcher.getService(ctx) : null;
}
//在Context中静态注册系统服务,仅在静态初始化期间调用此方法
private static <T> void registerService(String serviceName, Class<T> serviceClass,
ServiceFetcher<T> serviceFetcher) {
SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);
SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);
}
//静态类内部注册wifi服务
static{
...
registerService(Context.WIFI_SERVICE, WifiManager.class,
new CachedServiceFetcher<WifiManager>() {
@Override
public WifiManager createService(ContextImpl ctx) throws ServiceNotFoundException {
IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_SERVICE);
//从缓存列表里或service_manager进程里进行查找IBinder
IWifiManager service = IWifiManager.Stub.asInterface(b);
return new WifiManager(ctx.getOuterContext(), service,
ConnectivityThread.getInstanceLooper());
}});
...
}
asInterface #IWifiManager
这里通过Binder里的辅助类Stub来进行接口的实现,在此查询本地是否有实现该接口,如果是同一个线程的,则尝试为此Binder对象检索接口的本地实现。如果返回null,则需要实例化代理类,这个代理对象中将的方法会通过调用transact方法来进行内核态的切换,,简单来说这里实现了Binder通信的客户端接口的封装。涉及到Binder机制的问题就不在此细细展开了。
protected private static IWifiManager asInterface(IBinder var0) {
if (var0 == null) {
return null;
} else {
IInterface var1 = var0.queryLocalInterface("android.net.wifi.IWifiManager");
return (IWifiManager)(var1 != null && var1 instanceof IWifiManager ? (IWifiManager)var1 : new IWifiManager.Stub.Proxy(var0));
}
}
WifiManager #WifiManager
到此我们需要知道的一点就是我们想要的wifiManager实例已经在创建相应服务后拿到了。而此处通过传入服务和线程loop等进行实例话,这里稍微需要注意的是mService,因为是与底层利用Binder通信的主要方式,所以该类里会经常见到。
public WifiManager(Context context, IWifiManager service, Looper looper) {
mContext = context;
mService = service;
mLooper = looper;
mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;
}
Wifi启动(启用)
因为我们仅为Android端Nactive的一个处理,所以对底层驱动如何调用C语言去进行一个WiFi的启用和关闭就不拓展了,我们只需要了解Android本身的处理机制就行,而对于底层硬件的相关逻辑就简要概述即可。
WiFi启动处理流程如下:
在上面我们已经通过系统服务拿到了想要的wifimanager实例,后面可以利用这个实例开始为所欲为了。最开始应该就是需要进行setWifiEnable()也就是开启WiFi。
wifiManager.setWifiEnabled(true);
使用前需要权限申请(非隐私权限,因此不用考虑6.0的动态权限管理)
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
setWifiEnabled #WifiManager
那么快快的到我们分析之路吧,首先是启用或禁用Wi-Fi,下面无非是异常的检查,进而调用之前实例化获取的mService来进行进一步的启用。
public boolean setWifiEnabled(boolean enabled) {
try {
return mService.setWifiEnabled(mContext.getOpPackageName(), enabled);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
RemoteException是在执行远程方法调用期间可能发生的许多与通信相关的异常的公共超类。 远程接口的每个方法(扩展java.rmi.Remote的接口)必须在其throws子句中列出RemoteException 。
从版本1.4开始,此异常已经过改进,以符合通用异常链机制。 可以在施工时提供并通过公共detail字段访问的“包裹的远程异常”现在称为原因 ,并且可以通过Throwable.getCause()方法以及前述的“遗留字段”来访问。
setWifiEnabled #IWifiManager
在此处客户端就要开始自己的通信表演了,简单来说就是进行将要传入的信息进行序列化(Parcel),然后借助IBinder和token,对应WiFiServie的token是android.net.wifi.IWifiManager,进行信息通信,来实现WiFi的一个启动,在Binder里一个Client的基本操作也到此为止了。
public synchronized boolean setWifiEnabled(String var1, boolean var2) throws RemoteException {
Parcel var3 = Parcel.obtain();//创建或获取Parcel对象,此处是要发送到目标的编组数据
Parcel var4 = Parcel.obtain();//创建或获取Parcel对象,此处是要从目标接收的编组数据
boolean var5;
int var14;
label80: {
Throwable var10000;//异常检测
label84: {
IBinder var12;//负责通信
boolean var10001;
try {
var3.writeInterfaceToken("android.net.wifi.IWifiManager"); //通信token
var3.writeString(var1);//写入的包名
var3.writeInt(var2);//虽然是boolen,以int(0,1)方式写入
var12 = this.mRemote;
} catch (Throwable var11) {
var10000 = var11;
var10001 = false;
break label84;
}
var5 = false;
label75:
try {
var12.transact(25, var3, var4, 0);//将封装的信息进行发送
var4.readException(); //接收的信息进行异常检查
var14 = var4.readInt();
break label80; //通信完成则打断80循环
} catch (Throwable var10) {
var10000 = var10;
var10001 = false;
break label75;
}
}
Throwable var13 = var10000;
var4.recycle();
var3.recycle();
throw var13;
}
if (var14 != 0) {
var5 = true;
}
var4.recycle();//序列化结束回收
var3.recycle();//序列化结束回收
return var5;//返回通信结果
}
label标签用法是为了在多重循环中方便的使用 break 和coutinue 。
到此我们其实已经完成了Client也就是Android Native上层对WiFi一个启用控制的流程了,后续至于驱动如何解析我们发出的指令我们就不多加讨论,目前关注WiFi的一个大体逻辑即可。