WifiManage hotspot設置分析
高通MSM8226 平臺提供了對hotspot的設置,通過setting->more->Tethering&portable hotspot的Portable Wi-Fihotspot 可以設置手機WIFI提供熱點接入的功能,那麼具體流程與原理是什麼呢?
在\packages\apps\Settings\src\com\android\settings\wifi\WifiApEnabler.java文件中可以看到相關的具體的設置函數setSoftapEnabled,代碼大致如下
/*如果hotspot 設置使能,並且現在wificlient出於打開狀態,首先關閉WIFI*/
int wifiState =mWifiManager.getWifiState();
if (enable && ((wifiState == WifiManager.WIFI_STATE_ENABLING) ||
(wifiState == WifiManager.WIFI_STATE_ENABLED))){
mWifiManager.setWifiEnabled(false); }
/*調用這個WIFIManager類的接口設置AP使能,一會我們主要分析這個函數的實現*/
if (mWifiManager.setWifiApEnabled(null, enable)) {
/* Disable here, enabled on receiving success broadcast */
mCheckBox.setEnabled(false);
} else {
mCheckBox.setSummary(R.string.wifi_error);
}
/*當禁止AP時,查看之前WIFIclient狀態,並恢復*/
if (!enable) {
if (wifiSavedState == 1) {
mWifiManager.setWifiEnabled(true);
Settings.Global.putInt(cr,Settings.Global.WIFI_SAVED_STATE, 0);
}
}
看來是WiFiManager這個類提供了setWifiApEnabled的接口,下面我從framework開始分析setWifiApEanble這個函數,
這個函數位於\frameworks\base\wifi\java\android\net\wifi\WifiManager.java, 函數實現如下
/*通過mService類又調用了setWifiApEnabled*/
public boolean setWifiApEnabled(WifiConfiguration wifiConfig, booleanenabled) {
try {
mService.setWifiApEnabled(wifiConfig, enabled);
return true;
} catch (RemoteException e) {
return false;
}
}
好的,看看mService是一個什麼東西,其定義如下
IWifiManagermService;
??? IWifiManager是什麼,通過搜索他的定義,無果,後來發現,在WifiManager.java同名目錄下有一個IWifiManager.aidl文件,.aidl 全稱android 接口描述語言,類似j2ee中的RMI, RMI是通過序列化傳遞對象實現跨進程數據共享,AIDL通過定義接口實現跨進程的數據共享 IWifiManager.aidl部分文件如下
packageandroid.net.wifi;
importandroid.net.wifi.PPPOEConfig;
importandroid.net.wifi.PPPOEInfo;
importandroid.net.wifi.WifiInfo;
importandroid.net.wifi.WifiConfiguration;
importandroid.net.wifi.ScanResult;
importandroid.net.DhcpInfo;
importandroid.os.Messenger;
importandroid.os.WorkSource;
/*這些個接口提供訪問控制wifi的鏈接*/
interfaceIWifiManager
{
List<WifiConfiguration>getConfiguredNetworks();
int addOrUpdateNetwork(in WifiConfigurationconfig);
boolean removeNetwork(int netId);
boolean enableNetwork(int netId, booleandisableOthers);
boolean disableNetwork(int netId);
boolean pingSupplicant();
……此處省略N個字
}
通過搜索可以知道,其調用WifiService.java中的函數,代碼如下
public voidsetWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
enforceChangePermission();
mWifiController.obtainMessage(CMD_SET_AP,enabled ? 1 : 0, 0, wifiConfig).sendToTarget();
}
可以看出,該函數通過CMD_SET_AP 命令進行設置,mWifiController.obtainMessage將message發送到消息處理函數,進一步的處理在WifiController中實現,WifiController繼承了StateMachine,在StateMachine中實現了obtainMessage方法。該方法通過Message實現
Message.obtain(mSmHandler,what);
最終的調用結果如下
public staticMessage obtain(Handler h, int what,
int arg1, int arg2, Object obj) {
Message m = obtain();
m.target = h;
m.what = what;
m.arg1 = arg1;
m.arg2 = arg2;
m.obj = obj;
return m;
}
實際上對Message進行了初始化,信息通過Message對象的sendToTarget發送出去。
這個消息通過wificontroll.java中的ApStaDisabledState類中消息處理函數進行處理,代碼如下
case CMD_SET_AP:
if (msg.arg1 == 1) {
mWifiStateMachine.setHostApRunning((WifiConfiguration) msg.obj,
true);
transitionTo(mApEnabledState);
}
break;
主要通過調用WifiStateMachine中的方法實現對hostAp的設置,該函數定義如下
public voidsetHostApRunning(WifiConfiguration wifiConfig, boolean enable) {
if (enable) {
sendMessage(CMD_START_AP,wifiConfig);
} else {
sendMessage(CMD_STOP_AP);
}
}
又是message,繼續搜索CMD_START_AP,這個類的子類InitialState中的processMessage函數有對這個命令字的處理
case CMD_START_AP:
if(mWifiNative.loadDriver()) {
setWifiApState(WIFI_AP_STATE_ENABLING);
transitionTo(mSoftApStartingState);
} else {
loge("Failed toload driver for softap");
}
mWifiNative.loadDriver通過調用JNI接口加載驅動,其實現在android_net_wifi_Wifi.cpp文件的android_net_wifi_loadDriver函數,實現如下
staticJNINativeMethod gWifiMethods[] = {
/* name, signature, funcPtr */
{ "loadDriver","()Z", (void*)android_net_wifi_loadDriver },
此處省略N個字
}
setWifiApState(WIFI_AP_STATE_ENABLING);函數實現對WIFI狀態向其他模塊的通知,之後通過transitionTo(mSoftApStartingState); 進入到AP 啓動中的狀態,SoftApStartingState狀態的enter函數中,這個函數很關鍵,走讀到現在才發現最爲關鍵的部分。
public voidenter() {
final Message message =getCurrentMessage();
/*如果要開啓AP*/
if (message.what == CMD_START_AP) {
final WifiConfiguration config= (WifiConfiguration) message.obj;
/*obj這個參數實際上剛開始mWifiManager.setWifiApEnabled(null,enable)的null,所以這個時候爲空,走這個分支*/
if (config == null) {
if (startSafeChannel!=0) {
Log.e(TAG, "CallingsetChannelRange ---CMD_START_AP SoftApStartingState()");
setChannelRange(startSafeChannel, endSafeChannel , 0);
}
mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG);
} else {/*沒走到這裏*/
mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);
startSoftApWithConfig(config);
}
} else {
throw newRuntimeException("Illegal transition to SoftApStartingState: " +message);
}
}
setChannelRange應該是wifichannel情況的一些設置,我們暫時不管他。
從上面代碼中看出,mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG)函數進一步設置,好吧,繼續看CMD_REQUEST_AP_CONFIG,
這個AP設置請求的命令實際被WifiApConfigStore接收,如下
caseWifiStateMachine.CMD_REQUEST_AP_CONFIG:
mReplyChannel.replyToMessage(message,
WifiStateMachine.CMD_RESPONSE_AP_CONFIG, mWifiApConfig);
可以看出這實際上又返回了一個CMD_REQUEST_AP_CONFIG消息給SoftApStartingState狀態,在將焦點集中到這個類的消息處理函數
caseWifiStateMachine.CMD_RESPONSE_AP_CONFIG:
WifiConfiguration config =(WifiConfiguration) message.obj;
if (config != null) { /*獲取到了配置,正常不應該爲空,走到這裏*/
startSoftApWithConfig(config);
} else {
loge("Softapconfig is null!");
sendMessage(CMD_START_AP_FAILURE);
}
break;
繼續關注startSoftApWithConfig(config);函數,其實現如下
private void startSoftApWithConfig(finalWifiConfiguration config) {
// start hostapd on a seperate thread
new Thread(new Runnable() {
public void run() {
try {
mNwService.startAccessPoint(config,mInterfaceName);
} catch (Exception e) {
不用去關注,省略
}
if (DBG) log("Soft APstart successful");
sendMessage(CMD_START_AP_SUCCESS);
}
}).start();
}
這個函數實現,啓動了一個線程,可能配置需要時間,爲了不阻塞用戶而設計。startAccessPoint有用到了AIDL方法,調用其他進程函數,其調用了NetworkManagementService.java 的函數
public void startAccessPoint(
WifiConfiguration wifiConfig,String wlanIface) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
try {
wifiFirmwareReload(wlanIface,"AP"); 、/*加載AP*/
if (wifiConfig == null) {
mConnector.execute("softap", "set", wlanIface);
} else {
/*執行了下面的函數*/
mConnector.execute("softap", "set", wlanIface,wifiConfig.SSID,
getSecurityType(wifiConfig), new SensitiveArg(wifiConfig.preSharedKey));
}
mConnector.execute("softap", "startap");
} catch (NativeDaemonConnectorExceptione) {
throwe.rethrowAsParcelableException();
}
}
可以看出其通過softap進行了AP的相關設置,softap的設置代碼在NativeDaemonConnector類中,下面在framework是沒有了,我們現在可以看出hotspot的設置實際是放在HAL層實現的,這也符合android設計的易圖,因爲HAL層能夠隱藏廠商的設計,保護廠商的利益。簡單看一下execute成員的實現
publicNativeDaemonEvent[] execute(int timeout, String cmd, Object... args)
throwsNativeDaemonConnectorException {
final long startTime =SystemClock.elapsedRealtime();
finalArrayList<NativeDaemonEvent> events = Lists.newArrayList();
final StringBuilder rawBuilder = newStringBuilder();
final StringBuilder logBuilder = newStringBuilder();
final int sequenceNumber =mSequenceNumber.incrementAndGet();
/*生成了命令*/
makeCommand(rawBuilder, logBuilder,sequenceNumber, cmd, args);
final String rawCmd =rawBuilder.toString();
final String logCmd =logBuilder.toString();
log("SND -> {" + logCmd + "}");
synchronized (mDaemonLock) {
if (mOutputStream == null) {
throw newNativeDaemonConnectorException("missing output stream");
} else {
try {
mOutputStream.write(rawCmd.getBytes(Charsets.UTF_8));
} catch (IOException e) {
throw newNativeDaemonConnectorException("problem sending command", e);
}
}
}
NativeDaemonEvent event = null;
do {
event =mResponseQueue.remove(sequenceNumber, timeout, logCmd);
if (event == null) {
loge("timed-out waitingfor response to " + logCmd);
throw newNativeDaemonFailureException(logCmd, event);
}
log("RMV <- {" + event+ "}");
events.add(event);
} while (event.isClassContinue());
final long endTime =SystemClock.elapsedRealtime();
if (endTime - startTime >WARN_EXECUTE_DELAY_MS) {
loge("NDC Command {" +logCmd + "} took too long (" + (endTime - startTime) +"ms)");
}
if (event.isClassClientError()) {
throw newNativeDaemonArgumentException(logCmd, event);
}
if (event.isClassServerError()) {
throw newNativeDaemonFailureException(logCmd, event);
}
return events.toArray(newNativeDaemonEvent[events.size()]);
}
上面的函數大概意思是生成一個命令,並通過localSocket傳到對端。
在system目錄中實現了對softAP的具體設置,實現爲SoftapController.cpp文件的SoftapController::setSoftap函數
從整個分析過程來看,androidframework運用了大量的AIDL通信,message消息機制,以及狀態機處理方法,分析這些方法對理清整個設置流程至關重要。