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的一個大體邏輯即可,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/171169.html
標籤:其他
下一篇:canvas畫布縮放與移動
