昵称14797374 / android_wifi / Softap热点原理分析

分享

   

Softap热点原理分析

2013-12-24  昵称14797...
 

Softap热点原理分析

分类: Android Source 430人阅读 评论(0) 收藏 举报

目录(?)[+]

Android4.1.2


设置中开关:

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

|----private void startTethering()

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


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

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

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


框架:

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 == nullreturn;  
  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 == nullreturn;  
  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 == nullreturn;  
  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...]

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多
    喜欢该文的人也喜欢 更多

    ×
    ×

    ¥.00

    微信或支付宝扫码支付:

    开通即同意《个图VIP服务协议》

    全部>>