Softap熱點原理分析

Android4.1.2java


設置中開關:android

packages/apps/Settings/src/com/android/settings/TetherSettings.java
安全

|----private void startTethering()
網絡

|     |----mWifiApEnabler.setSoftapEnabled(true);
app


packages/apps/Settings/src/com/android/settings/wifi/WifiApEnabler.java
框架

|----public void setSoftapEnabled(boolean enable)less

|     |----mWifiManager.setWifiApEnabled(null, enable)socket


框架:ide

frameworks/base/wifi/java/android/net/wifi/WifiManager.java
函數

|----public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled)

|     |----mService.setWifiApEnabled(wifiConfig, enabled);


IWifiManager.aidl

|----void setWifiApEnabled(in WifiConfiguration wifiConfig, boolean enable);


frameworks/base/services/java/com/android/server/WifiService.java

|----public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled)

|     |----mWifiStateMachine.setWifiApEnabled(wifiConfig, enabled);


frameworks/base/wifi/java/android/net/wifi/WifiStateMachine.java

|----public void setWifiApEnabled(WifiConfiguration wifiConfig, boolean enable)

|     |----sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_AP_STATE_ENABLING, 0));

|     |----sendMessage(obtainMessage(CMD_START_AP, wifiConfig));


斗膽分析一下狀態機的運做

WifiStateMachine 繼承於StateMachine, 而在WifiStateMachine中未有對sendMessage方法的複寫,因此實現是使用父類的實現:

  1. /**   

  2.  * Enqueue a message to this state machine. 

  3.  */  

  4. public final void sendMessage(int what) {  

  5.     // mSmHandler can be null if the state machine has quit.  

  6.     if (mSmHandler == null) return;  

  7.   

  8.     mSmHandler.sendMessage(obtainMessage(what));  

  9. }      

  10.   

  11. /**   

  12.  * Enqueue a message to this state machine. 

  13.  */  

  14. public final void sendMessage(int what, Object obj) {  

  15.     // mSmHandler can be null if the state machine has quit.  

  16.     if (mSmHandler == null) return;  

  17.   

  18.     mSmHandler.sendMessage(obtainMessage(what,obj));  

  19. }      

  20.   

  21. /**   

  22.  * Enqueue a message to this state machine. 

  23.  */  

  24. public final void sendMessage(Message msg) {  

  25.     // mSmHandler can be null if the state machine has quit.  

  26.     if (mSmHandler == null) return;  

  27.   

  28.     mSmHandler.sendMessage(msg);  

  29. }  


可見,mSmHandler的定義是類SmHandler, 繼承於Handler, SmHandler對handleMessage進行了複寫,因此對於消息的接收處理應該是在SmHandler的handleMessage中:

  1. /** 

  2.  * Handle messages sent to the state machine by calling 

  3.  * the current state's processMessage. It also handles 

  4.  * the enter/exit calls and placing any deferred messages 

  5.  * back onto the queue when transitioning to a new state. 

  6.  */  

  7. @Override  

  8. public final void handleMessage(Message msg) {  

  9.     if (mDbg) Log.d(TAG, "handleMessage: E msg.what=" + msg.what);  

  10.   

  11.     /** Save the current message */  

  12.     mMsg = msg;  

  13.   

  14.     if (mIsConstructionCompleted) {    //正常的操做  

  15.         /** Normal path */  

  16.         processMsg(msg);  

  17.     } else if (!mIsConstructionCompleted &&  

  18.             (mMsg.what == SM_INIT_CMD) && (mMsg.obj == mSmHandlerObj)) {    //初始化操做  

  19.         /** Initial one time path. */  

  20.         mIsConstructionCompleted = true;  

  21.         invokeEnterMethods(0);  

  22.     } else {  

  23.         throw new RuntimeException("StateMachine.handleMessage: " +  

  24.                     "The start method not called, received msg: " + msg);  

  25.     }  

  26.     performTransitions();    //應用改變  

  27.   

  28.     if (mDbg) Log.d(TAG, "handleMessage: X");  

  29. }  


processMsg(msg):

  1. /**   

  2.  * Process the message. If the current state doesn't handle 

  3.  * it, call the states parent and so on. If it is never handled then 

  4.  * call the state machines unhandledMessage method. 

  5.  */  

  6. private final void processMsg(Message msg) {  

  7.     StateInfo curStateInfo = mStateStack[mStateStackTopIndex];    //獲取當前狀態  

  8.     if (mDbg) {  

  9.         Log.d(TAG, "processMsg: " + curStateInfo.state.getName());  

  10.     }      

  11.     while (!curStateInfo.state.processMessage(msg)) {    //判斷該消息是否處理  

  12.         /**   

  13.          * Not processed 

  14.          */  

  15.         curStateInfo = curStateInfo.parentStateInfo;  

  16.         if (curStateInfo == null) {  

  17.             /**   

  18.              * No parents left so it's not handled 

  19.              */  

  20.             mSm.unhandledMessage(msg);  

  21.             if (isQuit(msg)) {  

  22.                 transitionTo(mQuittingState);    //設置狀態  

  23.             }      

  24.             break;  

  25.         }      

  26.         if (mDbg) {  

  27.             Log.d(TAG, "processMsg: " + curStateInfo.state.getName());  

  28.         }      

  29.     }     

在WifiStateMachine中有不少狀態,截取幾個來看:

  1. /* Loading the driver */  

  2. private State mDriverUnloadedState = new DriverUnloadedState();  

  3. /* Driver load/unload failed */  

  4. private State mDriverFailedState = new DriverFailedState();  

  5. /* Driver loading */  

  6. private State mDriverLoadingState = new DriverLoadingState();  

  7. /* Driver loaded */  

  8. private State mDriverLoadedState = new DriverLoadedState();  

以上4個都是關於Wifi驅動加載與卸載的相關狀態,每個都有複寫本身的processMessage方法,好比DriverUnloadedState():

  1. @Override  

  2. public boolean processMessage(Message message) {  

  3.     if (DBG) log(getName() + message.toString() + "\n");  

  4.     switch (message.what) {  

  5.         case CMD_LOAD_DRIVER:  

  6.             transitionTo(mDriverLoadingState);  

  7.             break;  

  8.         default:  

  9.             return NOT_HANDLED;  

  10.     }  

  11.     return HANDLED;  

  12. }  


這說明,在狀態是「Wifi驅動已經成功卸載」時,系統只響應(handle)CMD_LOAD_DRIVER的消息,也就是驅動加載命令,其餘一律無論。很符合邏輯吧。

假設,在打開Wifi熱點的時候,驅動就是卸載的(默認狀態),那麼 sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_AP_STATE_ENABLING, 0));事後會來到這裏,也就會將新的狀態mDriverLoadingState加入狀態棧。隨後返回HANDLED,另外一種NOT_HANDLED就 不作討論了。那麼如今的流程變成了processMsg(msg) --> transitionTo(mDriverLoadingState) --> performTransitions(),因此在分析performTransitions()以前要先看看transitionTo(實如今父類 StateMachine中):

  1. /** @see StateMachine#transitionTo(IState) */  

  2. private final void transitionTo(IState destState) {  

  3.     mDestState = (State) destState;  

  4.     if (mDbg) Log.d(TAG, "transitionTo: destState=" + mDestState.getName());  

  5. }  


因爲State是IState的子類,因此這樣的參數傳遞進去沒有問題,mDestState目標狀態變成了mDriverLoadingState,而後是performTransitions()(仍是在父類StateMachine中):

  1.         /** 

  2.          * Do any transitions 

  3.          */  

  4.         private void performTransitions() {  

  5.             /** 

  6.              * If transitionTo has been called, exit and then enter 

  7.              * the appropriate states. We loop on this to allow 

  8.              * enter and exit methods to use transitionTo. 

  9.              */  

  10.             State destState = null;  

  11.             while (mDestState != null) {  //即transitionTo設置的新狀態 mDriverLoadingState  

  12.                 if (mDbg) Log.d(TAG, "handleMessage: new destination call exit");  

  13.   

  14.                 /** 

  15.                  * Save mDestState locally and set to null 

  16.                  * to know if enter/exit use transitionTo. 

  17.                  */  

  18.                 destState = mDestState;  

  19.                 mDestState = null;  

  20.   

  21.                 /** 

  22.                  * Determine the states to exit and enter and return the 

  23.                  * common ancestor state of the enter/exit states. Then 

  24.                  * invoke the exit methods then the enter methods. 

  25.                  */  

  26.                 StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState);  //將狀態裝入臨時隊列  

  27.                 invokeExitMethods(commonStateInfo);    //將該狀態以前的全部狀態所有退出  

  28.                 int stateStackEnteringIndex = moveTempStateStackToStateStack();    //把臨時隊列合併至標準隊列,而且返回界限值stateStaclEnteringIndex  

  29.                 invokeEnterMethods(stateStackEnteringIndex);    //遍歷執行自界限值到隊列頂部的全部狀態的enter方法,以下圖所示:  

  30. /* 

  31.     |-------------| 

  32. High|   stack_x   |  mStateStackTopIndex 

  33.     |-------------| 

  34.     |   .....     | 

  35.     |-------------| 

  36.     |   stack_y   |  stateStackEnteringIndex  以上都是從Temp臨時隊列合併來的 

  37.     |-------------| 

  38.     |   .....     | 

  39.     |-------------| 

  40. Low |   stack_1   | 

  41.     |-------------| 

  42. */  

  43.   

  44. /** 

  45.  * Since we have transitioned to a new state we need to have 

  46.  * any deferred messages moved to the front of the message queue 

  47.  * so they will be processed before any other messages in the 

  48.  * message queue. 

  49.  */ moveDeferredMessageAtFrontOfQueue(); //將全部延遲消息再次發送到隊列頂部,隨後清除延遲消息隊列。 }   

  50. /** 

  51.  * After processing all transitions check and 

  52.  * see if the last transition was to quit or halt. 

  53.  */  

  54.   

  55.             if (destState != null) {    //如下檢查狀態是不是需求退出或掛起的,是則進行相應處理  

  56.                 if (destState == mQuittingState) {  

  57.                     cleanupAfterQuitting();  

  58.   

  59.                 } else if (destState == mHaltingState) {  

  60.                     /** 

  61.                      * Call halting() if we've transitioned to the halting 

  62.                      * state. All subsequent messages will be processed in 

  63.                      * in the halting state which invokes haltedProcessMessage(msg); 

  64.                      */  

  65.                     mSm.halting();  

  66.                 }  

  67.             }  

  68.         }  


看了好多子函數,有點暈暈的。看得出來這個performTransitions()是對全部狀態進行處理的關鍵節點,可能同一時間會受到不少 Message,而這些Message所攜帶的不一樣狀態會被加入到一個臨時隊列中,而後會將標準隊列頂端到此狀態以前的全部狀態都退出(也就是觸發 exit()),並設置爲非活躍,而後剔除。以後會將臨時隊列合併入標準隊列,取得一個界限值,從界限值到隊列頂端依次激活(觸發enter())。其實 在sendMessage的同時,還有一種消息處理方式就是deferMessage,是對消息的延遲發送,最終會將消息加入到一個延遲消息隊列 mDeferredMessages中,每次的performTransitions()都會對延遲消息隊列進行從新發送而且清空它的隊列。最後,還會檢 測一下是否有特殊的狀態須要處理,如退出和掛起。


回到正題

WifiStateMachine.java

應該關注一下mDriverLoadingState了,前邊看到這是一個DriverLoadingState(),enter()的主要內容是一個工做線程:

  1.     new Thread(new Runnable() {  

  2.         public void run() {  

  3.             mWakeLock.acquire();    //整個過程須要wakelock保護  

  4.             //enabling state  

  5.             switch(message.arg1) {  

  6.                 case WIFI_STATE_ENABLING:    //打開WIFI  

  7.                     setWifiState(WIFI_STATE_ENABLING);  

  8.                     break;  

  9.                 case WIFI_AP_STATE_ENABLING:    //打開WIFI AP  

  10.                     setWifiApState(WIFI_AP_STATE_ENABLING);  

  11.                     break;  

  12.             }  

  13.   

  14.             if(mWifiNative.loadDriver()) {    // 加載Wifi驅動,WifiNative.java --> core/jni /android_net_wifi_Wifi.cpp --> hardware/libhardware_legacy/wifi.c 就是 insmod xxx.ko,也許帶參數blablabla  

  15.                 if (DBG) log("Driver load successful");  

  16.                 sendMessage(CMD_LOAD_DRIVER_SUCCESS);  

  17.             } else {  

  18.                 loge("Failed to load driver!");  

  19.                 switch(message.arg1) {  

  20.                     case WIFI_STATE_ENABLING:  

  21.                         setWifiState(WIFI_STATE_UNKNOWN);  

  22.                         break;  

  23.                     case WIFI_AP_STATE_ENABLING:  

  24.                         setWifiApState(WIFI_AP_STATE_FAILED);  

  25.                         break;  

  26.                 }  

  27.                 sendMessage(CMD_LOAD_DRIVER_FAILURE);  

  28.             }  

  29.             mWakeLock.release();  

  30.         }  

  31.     }).start();  

  32. }  


而這裏能夠快速的複習一下前邊的流程,加載成功後會sendMessage(CMD_LOAD_DRIVER_SUCCESS),失敗了就會發送 CMD_LOAD_DRIVER_FAILURE。當前的狀態就是mDriverLoadingState,因此是DriverLoadingState 的processMessage來處理這兩個消息了:

  1. @Override  

  2. public boolean processMessage(Message message) {  

  3.     if (DBG) log(getName() + message.toString() + "\n");  

  4.     switch (message.what) {  

  5.         case CMD_LOAD_DRIVER_SUCCESS:  

  6.             transitionTo(mDriverLoadedState);  

  7.             break;  

  8.         case CMD_LOAD_DRIVER_FAILURE:  

  9.             transitionTo(mDriverFailedState);  

  10.             break;  

  11.         case CMD_LOAD_DRIVER:  

  12.         case CMD_UNLOAD_DRIVER:  

  13.         case CMD_START_SUPPLICANT:  

  14.         case CMD_STOP_SUPPLICANT:  

  15.         case CMD_START_AP:  

  16.         case CMD_STOP_AP:  

  17.         case CMD_START_DRIVER:  

  18.         case CMD_STOP_DRIVER:  

  19.         case CMD_SET_SCAN_MODE:  

  20.         case CMD_SET_SCAN_TYPE:  

  21.         case CMD_SET_COUNTRY_CODE:  

  22.         case CMD_SET_FREQUENCY_BAND:  

  23.         case CMD_START_PACKET_FILTERING:  

  24.         case CMD_STOP_PACKET_FILTERING:  

  25.             deferMessage(message);  

  26.             break;  

  27.         default:  

  28.             return NOT_HANDLED;  

  29.     }  

  30.     return HANDLED;  

  31. }  


因而可知,加載成功後狀態就變爲mDriverLoadedState,失敗了狀態就是mDriverFailedState。回到DriverLoadingState的enter,setWifiApState:

  1. private void setWifiApState(int wifiApState) {  

  2.     final int previousWifiApState = mWifiApState.get();  

  3.   

  4.     try {  

  5.         if (wifiApState == WIFI_AP_STATE_ENABLED) {    //WIFI AP已經打開,則電池狀態開始記錄Wifi相關  

  6.             mBatteryStats.noteWifiOn();  

  7.         } else if (wifiApState == WIFI_AP_STATE_DISABLED) {    //WIFI AP已經關閉,則電池狀態對WIFI的記錄關閉  

  8.             mBatteryStats.noteWifiOff();  

  9.         }  

  10.     } catch (RemoteException e) {  

  11.         loge("Failed to note battery stats in wifi");  

  12.     }  

  13.   

  14.     // Update state  

  15.     mWifiApState.set(wifiApState);    //設置WIFI AP的狀態,原子狀態  

  16.   

  17.     if (DBG) log("setWifiApState: " + syncGetWifiApStateByName());  

  18.   

  19.   

  20.     //將狀態消息發送至WifiManager進行進一步處理。終於脫離了狀態機,回到WifiManager了。  

  21.     final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);  

  22.     intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);  

  23.     intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState);  

  24.     intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState);  

  25.     mContext.sendStickyBroadcast(intent);  

  26. }  


PS:經過sendBroadcast中發出的intent在Reciever註冊後才能正確收到,未註冊的時候不能被接收,即便後面再次註冊上也沒法接受到。而sendStickyBroadcast發出的Intent當Reciever註冊後就能收到Intent,即便註冊發生在廣播以後。也就是說sendStickyBroadcast安全性更高,可以保證廣播不會丟失,而sendBroadcast有必定危險。

好 的,分析了這麼久,只是有一條sendMessage(obtainMessage(CMD_LOAD_DRIVER, WIFI_AP_STATE_ENABLING, 0)),發送出狀態廣播給別人獲取,在系統中一個很好的例子是桌面電源控件對這個狀態進行接收,能夠直觀的理解爲當ing的狀態時某按鈕是不可用的。

而後纔是真正的開啓動做:

sendMessage(obtainMessage(CMD_START_AP, wifiConfig));

假設加載成功,當前狀態變成了mDriverLoadedState,那麼去DriverLoadedState的processMessage尋找這個Message的處理方法:

  1. case CMD_START_AP:  

  2.     transitionTo(mSoftApStartingState);  

  3.     break;  


新的狀態,mSoftApStartingState:

  1. /* Soft ap is starting up */  

  2. private State mSoftApStartingState = new SoftApStartingState();  


  1. @Override  

  2. public void enter() {  

  3.     if (DBG) log(getName() + "\n");  

  4.     EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());  

  5.   

  6.     final Message message = getCurrentMessage();  

  7.     if (message.what == CMD_START_AP) {    //若是進入這個狀態而不是打開AP,那麼就直接拋出runtime異常,通常來講就是重啓了。又一次驗證了:不以結婚爲目的的談戀愛都是耍流氓。  

  8.         final WifiConfiguration config = (WifiConfiguration) message.obj;  

  9.   

  10.         if (config == null) {  

  11.             mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG);  

  12.         } else {  

  13.             mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);  

  14.             startSoftApWithConfig(config);  

  15.         }  

  16.     } else {  

  17.         throw new RuntimeException("Illegal transition to SoftApStartingState: " + message);  

  18.     }  

  19. }  


OK, config爲NULL,又是一個Message:

  1. mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG);  


在WifiStateMachine構造的時候對mWifiApConfigChannel設置了handler:

  1. mWifiApConfigChannel.connectSync(mContext, getHandler(), wifiApConfigStore.getMessenger());  


WifiApConfigStore.java

CMD_REQUEST_AP_CONFIG的消息處理是在WifiApConfigStore中處理的:

  1. class DefaultState extends State {  

  2.     public boolean processMessage(Message message) {  

  3.         switch (message.what) {  

  4.             case WifiStateMachine.CMD_SET_AP_CONFIG:  

  5.             case WifiStateMachine.CMD_SET_AP_CONFIG_COMPLETED:  

  6.                 Log.e(TAG, "Unexpected message: " + message);  

  7.                 break;  

  8.             case WifiStateMachine.CMD_REQUEST_AP_CONFIG:  

  9.                 mReplyChannel.replyToMessage(message,  

  10.                         WifiStateMachine.CMD_RESPONSE_AP_CONFIG, mWifiApConfig);  

  11.                 break;  

  12.             default:  

  13.                 Log.e(TAG, "Failed to handle " + message);  

  14.                 break;  

  15.         }     

  16.         return HANDLED;  

  17.     }     

  18. }    


當前WIFI狀態機狀態爲SoftApStartingState,因此回覆消息在這裏處理:

  1. @Override     

  2. public boolean processMessage(Message message) {  

  3.     if (DBG) log(getName() + message.toString() + "\n");  

  4.     switch(message.what) {  

  5.         case CMD_LOAD_DRIVER:  

  6.         case CMD_UNLOAD_DRIVER:  

  7.         case CMD_START_SUPPLICANT:  

  8.         case CMD_STOP_SUPPLICANT:  

  9.         case CMD_START_AP:   

  10.         case CMD_STOP_AP:  

  11.         case CMD_START_DRIVER:  

  12.         case CMD_STOP_DRIVER:  

  13.         case CMD_SET_SCAN_MODE:  

  14.         case CMD_SET_SCAN_TYPE:  

  15.         case CMD_SET_COUNTRY_CODE:  

  16.         case CMD_SET_FREQUENCY_BAND:  

  17.         case CMD_START_PACKET_FILTERING:  

  18.         case CMD_STOP_PACKET_FILTERING:  

  19.         case CMD_TETHER_STATE_CHANGE:  

  20.             deferMessage(message);  

  21.             break;  

  22.         case WifiStateMachine.CMD_RESPONSE_AP_CONFIG:  

  23.             WifiConfiguration config = (WifiConfiguration) message.obj;    //設置文件就是WifiApConfigStore中的mWifiApConfig  

  24.             if (config != null) {  

  25.                 startSoftApWithConfig(config);    //若是配置文件存在就繼續開啓AP  

  26.             } else {  

  27.                 loge("Softap config is null!");    //若是配置文件爲空則開啓失敗,發送個消息CMD_START_AP_FAILURE,仍是在本狀態中處理  

  28.                 sendMessage(CMD_START_AP_FAILURE);  

  29.             }  

  30.             break;  

  31.         case CMD_START_AP_SUCCESS:  

  32.             setWifiApState(WIFI_AP_STATE_ENABLED);  

  33.             transitionTo(mSoftApStartedState);  

  34.             break;  

  35.         case CMD_START_AP_FAILURE:  

  36.             // initiate driver unload  

  37.             sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0));  //卸載驅動,並更改狀態爲AP開啓失敗  

  38.             break;  

  39.         default:  

  40.             return NOT_HANDLED;  

  41.     }     

  42.     return HANDLED;  

  43. }  


配置文件

這裏的配置文件是經過WifiManager的setWifiApConfiguration接口生成的:

frameworks/base/wifi/java/android/net/wifi/WifiManager.java

  1. /**   

  2.  * Sets the Wi-Fi AP Configuration. 

  3.  * @return {@code true} if the operation succeeded, {@code false} otherwise 

  4.  * 

  5.  * @hide Dont open yet 

  6.  */  

  7. public boolean setWifiApConfiguration(WifiConfiguration wifiConfig) {  

  8.     try {  

  9.         mService.setWifiApConfiguration(wifiConfig);  

  10.         return true;  

  11.     } catch (RemoteException e) {   

  12.         return false;  

  13.     }      

  14. }   


mService爲IWifiManager,該接口定義以下:

  1. void setWifiApConfiguration(in WifiConfiguration wifiConfig);  


而實現爲WifiService

  1. public class WifiService extends IWifiManager.Stub  


  1. /**   

  2.  * see {@link WifiManager#setWifiApConfiguration(WifiConfiguration)} 

  3.  * @param wifiConfig WifiConfiguration details for soft access point 

  4.  */  

  5. public void setWifiApConfiguration(WifiConfiguration wifiConfig) {  

  6.     enforceChangePermission();  

  7.     if (wifiConfig == null)  

  8.         return;  

  9.     mWifiStateMachine.setWifiApConfiguration(wifiConfig);  

  10. }   


真是的實現有拋給了WifiStateMachine:

  1. public void setWifiApConfiguration(WifiConfiguration config) {  

  2.     mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);  

  3. }   


消息交給WifiApConfigStore處理,而

  1. WifiApConfigStore(Context context, Handler target) {  

  2.     super(TAG, target.getLooper());  

  3.   

  4.     mContext = context;  

  5.     addState(mDefaultState);  

  6.         addState(mInactiveState, mDefaultState);  

  7.         addState(mActiveState, mDefaultState);  

  8.   

  9.     setInitialState(mInactiveState);  

  10. }  


WifiApConfigStore在構造的時候分mDefaultState分配了兩個子狀態mInactiveState, mActiveState, 初始化狀態爲mInactiveState。

  1. class InactiveState extends State {  

  2.     public boolean processMessage(Message message) {  

  3.         switch (message.what) {  

  4.             case WifiStateMachine.CMD_SET_AP_CONFIG:  

  5.                 mWifiApConfig = (WifiConfiguration) message.obj;  

  6.                 transitionTo(mActiveState);    //觸發ActiveState.enter()  

  7.                 break;  

  8.             default:  

  9.                 return NOT_HANDLED;  

  10.         }     

  11.         return HANDLED;  

  12.     }  

  13. }  


  1. class ActiveState extends State {  

  2.     public void enter() {  

  3.         new Thread(new Runnable() {  

  4.             public void run() {  

  5.                 writeApConfiguration(mWifiApConfig);  

  6.                 sendMessage(WifiStateMachine.CMD_SET_AP_CONFIG_COMPLETED);  

  7.             }  

  8.         }).start();  

  9.     }  


writeApConfiguration實現:

  1. private void writeApConfiguration(final WifiConfiguration config) {  

  2.     DataOutputStream out = null;  

  3.     try {  

  4.         out = new DataOutputStream(new BufferedOutputStream(  

  5.                     new FileOutputStream(AP_CONFIG_FILE)));  

  6.   

  7.         out.writeInt(AP_CONFIG_FILE_VERSION);  

  8.         out.writeUTF(config.SSID);  

  9.         int authType = config.getAuthType();  

  10.         out.writeInt(authType);  

  11.         if(authType != KeyMgmt.NONE) {  

  12.             out.writeUTF(config.preSharedKey);  

  13.         }  

  14.     } catch (IOException e) {  

  15.         Log.e(TAG, "Error writing hotspot configuration" + e);  

  16.     } finally {  

  17.         if (out != null) {  

  18.             try {  

  19.                 out.close();  

  20.             } catch (IOException e) {}  

  21.         }  

  22.     }  

  23. }  


默認文件路徑即爲/misc/wifi/softap.conf,寫好配置文件後發送WifiStateMachine.CMD_SET_AP_CONFIG_COMPLETED,本身發本身收了:

  1. public boolean processMessage(Message message) {  

  2.     switch (message.what) {  

  3.         //TODO: have feedback to the user when we do this  

  4.         //to indicate the write is currently in progress  

  5.         case WifiStateMachine.CMD_SET_AP_CONFIG:  

  6.             deferMessage(message);  

  7.             break;  

  8.         case WifiStateMachine.CMD_SET_AP_CONFIG_COMPLETED:    //修改完後切換狀態到InactiveState  

  9.             transitionTo(mInactiveState);  

  10.             break;  

  11.         default:  

  12.             return NOT_HANDLED;  

  13.     }  

  14.     return HANDLED;  

  15. }  


這樣配置文件就配置完了,結果是保存在mWifiConfig中的。


帶着配置文件開啓AP

frameworks/base/wifi/java/android/net/wifi/WifiStateMachine.java      startSoftApWithConfig

  1. /* Current design is to not set the config on a running hostapd but instead 

  2.  * stop and start tethering when user changes config on a running access point 

  3.  * 

  4.  * TODO: Add control channel setup through hostapd that allows changing config 

  5.  * on a running daemon 

  6.  */  

  7. private void startSoftApWithConfig(final WifiConfiguration config) {  

  8.     // start hostapd on a seperate thread  

  9.     new Thread(new Runnable() {  

  10.         public void run() {  

  11.             try {  

  12.                 mNwService.startAccessPoint(config, mInterfaceName, SOFTAP_IFACE);  

  13.             } catch (Exception e) {  

  14.                 loge("Exception in softap start " + e);  

  15.                 try {  

  16.                     mNwService.stopAccessPoint(mInterfaceName);  

  17.                     mNwService.startAccessPoint(config, mInterfaceName, SOFTAP_IFACE);  

  18.                 } catch (Exception e1) {  

  19.                     loge("Exception in softap re-start " + e1);  

  20.                     sendMessage(CMD_START_AP_FAILURE);  

  21.                     return;  

  22.                 }  

  23.             }  

  24.             if (DBG) log("Soft AP start successful");  

  25.             sendMessage(CMD_START_AP_SUCCESS);  

  26.         }  

  27.     }).start();  

  28. }  


邏輯就是嘗試開啓,若是發生錯誤就嘗試重啓,若是再錯誤就認可失敗,發送失敗狀態,若是沒錯誤就發送成功的消息。關鍵在mNwService的startAccessPoint方法中。

config, mInterfaceName, SOFTAP_IFACE

這三個參數:config爲傳遞下來的配置文件,SOFTAP_IFACE爲字符串wl0.1,mInterfaceName爲WifiStateMachine構造時傳遞下來的參數,而這個構造動做由WifiService構造的時候發起:

  1.     WifiService(Context context) {  

  2.         mContext = context;  

  3.   

  4.         mInterfaceName =  SystemProperties.get("wifi.interface", "wlan0");  

  5.   

  6.         mWifiStateMachine = new WifiStateMachine(mContext, mInterfaceName);  

  7.         mWifiStateMachine.enableRssiPolling(true);  

  8. ...  

  9. }  


可見,這個mInterfaceName由prop wifi.interface控制,如咱們常常能在build.prop中看到wifi.interface=eth0/wlan0等,若是沒有會默認給wlan0。

接下來看startAccessPoint的實現(frameworks/base/services/java/com/android/server/NetworkManagementService.java):

  1. @Override  

  2. public void startAccessPoint(  

  3.         WifiConfiguration wifiConfig, String wlanIface, String softapIface) {  

  4.     mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);  

  5.     try {  

  6.         Resources resources = mContext.getResources();  

  7.         if (resources.getBoolean(com.android.internal.R.bool.config_wifiApFirmwareReload))  

  8.             wifiFirmwareReload(wlanIface, "AP");  

  9.         if (resources.getBoolean(com.android.internal.R.bool.config_wifiApStartInterface))  

  10.             mConnector.execute("softap", "start", wlanIface);  

  11.         if (wifiConfig == null) {  

  12.             mConnector.execute("softap", "set", wlanIface, softapIface);  

  13.         } else {  

  14.             mConnector.execute("softap", "set", wlanIface, softapIface, wifiConfig.SSID,  

  15.                     getSecurityType(wifiConfig), wifiConfig.preSharedKey);  

  16.         }      

  17.         mConnector.execute("softap", "startap");  

  18.     } catch (NativeDaemonConnectorException e) {   

  19.         throw e.rethrowAsParcelableException();  

  20.     }      

  21. }  


config_wifiApFirmwareReload、config_wifiApStartInterface都是能夠用戶自定義的xml配置接口,默認在frameworks/base/core/res/res/values/config.xml中,默認如:

  1. <!-- Boolean indicating whether Softap requires reloading AP firmware -->  

  2. <bool name="config_wifiApFirmwareReload">true</bool>  

  3.   

  4. <!-- Boolean indicating whether the start command should be called on the wireless interface  

  5.      when starting the SoftAp -->  

  6. <bool name="config_wifiApStartInterface">false</bool>  


想關聯的幾個函數有:

  1. private static String getSecurityType(WifiConfiguration wifiConfig) {    //獲取網絡安全類型  

  2.     switch (wifiConfig.getAuthType()) {  

  3.         case KeyMgmt.WPA_PSK:  

  4.             return "wpa-psk";  

  5.         case KeyMgmt.WPA2_PSK:  

  6.             return "wpa2-psk";  

  7.         default:  

  8.             return "open";  

  9.     }      

  10. }      

  11.   

  12. /* @param mode can be "AP", "STA" or "P2P" */  

  13. @Override  

  14. public void wifiFirmwareReload(String wlanIface, String mode) {    //根據不一樣模式裝在不一樣的固件(若是有須要的話)  

  15.     mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);  

  16.     try {  

  17.         mConnector.execute("softap", "fwreload", wlanIface, mode);  

  18.     } catch (NativeDaemonConnectorException e) {   

  19.         throw e.rethrowAsParcelableException();  

  20.     }      

  21. }   


經過以上不難看出,最終都是經過mConnector.execute來執行命令。

  1. /** 

  2.  * Constructs a new NetworkManagementService instance 

  3.  * 

  4.  * @param context  Binder context for this service 

  5.  */  

  6. private NetworkManagementService(Context context) {  

  7.     mContext = context;  

  8.   

  9.     if ("simulator".equals(SystemProperties.get("ro.product.device"))) {  

  10.         return;  

  11.     }  

  12.   

  13.     mConnector = new NativeDaemonConnector(  

  14.             new NetdCallbackReceiver(), "netd", 10, NETD_TAG, 160);  

  15.     mThread = new Thread(mConnector, NETD_TAG);  

  16.   

  17.     // Add ourself to the Watchdog monitors.  

  18.     Watchdog.getInstance().addMonitor(this);  

  19. }  


mConnector是在構造時生成的NativeDaemonConnector對象,查看一下NativeDaemonConnector的構造過程 (frameworks/base/services/java/com/android/server /NativeDaemonConnector.java):

  1. NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket,  

  2.         int responseQueueSize, String logTag, int maxLogSize) {  

  3.     mCallbacks = callbacks;  

  4.     mSocket = socket;  

  5.     mResponseQueue = new ResponseQueue(responseQueueSize);  

  6.     mSequenceNumber = new AtomicInteger(0);  

  7.     TAG = logTag != null ? logTag : "NativeDaemonConnector";  

  8.     mLocalLog = new LocalLog(maxLogSize);  

  9. }    


1.分別在handleMessage和listenToSocket的時候調用回調對象的onEvent和onDaemonConnected方法。而 監聽socket的服務被建立後就已經開出一個線程始終監聽了。在這裏爲new NetdCallbackReceiver();

2.mSocket也就是在NetworkManagementService中始終監聽的那個local socket。在這裏爲netd(/dev/socket/netd);

3.mResponseQueue是新建了一個命令隊列ResponseQueue,傳遞金的參數responseQueuesize就是這個隊列的容量上限。這個子類算上構造在內總共4個方法:
    a.構造

    b.添加命令

    c.移除命令

    d.打印隊列信息

4.mSequeueceNumber做爲指令執行計數器,是個原子量, 防止線程操做混亂;

5.日誌標籤

6.日誌容量

構造完成後,會new出一個線程,這個線程的工做就是調用listenToSocket。最後會使用看門狗來保護這個服務。


回到主線,默認狀況下,並有有效的配置文件,打開WIFI AP須要執行兩條命令:

  1. mConnector.execute("softap", "fwreload", wlanIface, mode);  

  2. mConnector.execute("softap", "set", wlanIface, softapIface, wifiConfig.SSID,  

  3.                         getSecurityType(wifiConfig), wifiConfig.preSharedKey);  


逐個分析一下:

固件重載

wlanIface就是prop指定的wifi.interface,默認爲wlan0,mode爲"AP",共計四個參數。

這兩條命令都會最終執行到這裏:

  1. public NativeDaemonEvent[] execute(int timeout, String cmd, Object... args)  

  2.         throws NativeDaemonConnectorException {  

  3.     final ArrayList<NativeDaemonEvent> events = Lists.newArrayList();  

  4.   

  5.     final int sequenceNumber = mSequenceNumber.incrementAndGet();    //命令計數器加一併返回  

  6.     final StringBuilder cmdBuilder =  

  7.             new StringBuilder(Integer.toString(sequenceNumber)).append(' ');  

  8.     final long startTime = SystemClock.elapsedRealtime();  //返回的是自從系統啓動到當前的時間  

  9.   

  10.     makeCommand(cmdBuilder, cmd, args);    //將全部參數整合成一條命令,放置在cmdBuilder中  

  11.   

  12.     final String logCmd = cmdBuilder.toString(); /* includes cmdNum, cmd, args */  

  13.     log("SND -> {" + logCmd + "}");  

  14.   

  15.     cmdBuilder.append('\0');    //給字符串來個尾巴,而後化做真正的字符串sentCmd  

  16.     final String sentCmd = cmdBuilder.toString(); /* logCmd + \0 */  

  17.   

  18.     synchronized (mDaemonLock) {  

  19.         if (mOutputStream == null) {    //mOutputStraem是netd的輸出通道  

  20.             throw new NativeDaemonConnectorException("missing output stream");  

  21.         } else {  

  22.             try {  

  23.                 mOutputStream.write(sentCmd.getBytes(Charsets.UTF_8));    //將命令發送出去  netd socket  

  24.             } catch (IOException e) {  

  25.                 throw new NativeDaemonConnectorException("problem sending command", e);  

  26.             }  

  27.         }  

  28.     }  

  29.   

  30.     NativeDaemonEvent event = null;  

  31.     do {  

  32.         event = mResponseQueue.remove(sequenceNumber, timeout, sentCmd);  //從命令隊列中刪除已經發送出去的命令  

  33.         if (event == null) {  

  34.             loge("timed-out waiting for response to " + logCmd);  

  35.             throw new NativeDaemonFailureException(logCmd, event);  

  36.         }  

  37.         log("RMV <- {" + event + "}");  

  38.         events.add(event);  

  39.     } while (event.isClassContinue());  

  40.   

  41.     final long endTime = SystemClock.elapsedRealtime();  

  42.     if (endTime - startTime > WARN_EXECUTE_DELAY_MS) {  

  43.         loge("NDC Command {" + logCmd + "} took too long (" + (endTime - startTime) + "ms)");  

  44.     }  

  45.   

  46.     if (event.isClassClientError()) {  

  47.         throw new NativeDaemonArgumentException(logCmd, event);  

  48.     }  

  49.     if (event.isClassServerError()) {  

  50.         throw new NativeDaemonFailureException(logCmd, event);  

  51.     }  

  52.   

  53.     return events.toArray(new NativeDaemonEvent[events.size()]);  

  54. }  


如今看來,全部命令都是經過netd socket發送出去。可是這個socket是誰來接收呢?


netd Socket

system/netd

[to be continued...]

相關文章
相關標籤/搜索