设置中开关:
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方法的复写,所以实现是使用父类的实现:
- /**
- * Enqueue a message to this state machine.
- */
- public final void sendMessage(int what) {
- // mSmHandler can be null if the state machine has quit.
- if (mSmHandler == null) return;
- mSmHandler.sendMessage(obtainMessage(what));
- }
- /**
- * Enqueue a message to this state machine.
- */
- public final void sendMessage(int what, Object obj) {
- // mSmHandler can be null if the state machine has quit.
- if (mSmHandler == null) return;
- mSmHandler.sendMessage(obtainMessage(what,obj));
- }
- /**
- * Enqueue a message to this state machine.
- */
- public final void sendMessage(Message msg) {
- // mSmHandler can be null if the state machine has quit.
- if (mSmHandler == null) return;
- mSmHandler.sendMessage(msg);
- }
可见,mSmHandler的定义是类SmHandler, 继承于Handler, SmHandler对handleMessage进行了复写,所以对于消息的接收处理应该是在SmHandler的handleMessage中:
- /**
- * Handle messages sent to the state machine by calling
- * the current state's processMessage. It also handles
- * the enter/exit calls and placing any deferred messages
- * back onto the queue when transitioning to a new state.
- */
- @Override
- public final void handleMessage(Message msg) {
- if (mDbg) Log.d(TAG, "handleMessage: E msg.what=" + msg.what);
- /** Save the current message */
- mMsg = msg;
- if (mIsConstructionCompleted) { //正常的操作
- /** Normal path */
- processMsg(msg);
- } else if (!mIsConstructionCompleted &&
- (mMsg.what == SM_INIT_CMD) && (mMsg.obj == mSmHandlerObj)) { //初始化操作
- /** Initial one time path. */
- mIsConstructionCompleted = true;
- invokeEnterMethods(0);
- } else {
- throw new RuntimeException("StateMachine.handleMessage: " +
- "The start method not called, received msg: " + msg);
- }
- performTransitions(); //应用改变
- if (mDbg) Log.d(TAG, "handleMessage: X");
- }
processMsg(msg):
- /**
- * Process the message. If the current state doesn't handle
- * it, call the states parent and so on. If it is never handled then
- * call the state machines unhandledMessage method.
- */
- private final void processMsg(Message msg) {
- StateInfo curStateInfo = mStateStack[mStateStackTopIndex]; //获取当前状态
- if (mDbg) {
- Log.d(TAG, "processMsg: " + curStateInfo.state.getName());
- }
- while (!curStateInfo.state.processMessage(msg)) { //判断该消息是否处理
- /**
- * Not processed
- */
- curStateInfo = curStateInfo.parentStateInfo;
- if (curStateInfo == null) {
- /**
- * No parents left so it's not handled
- */
- mSm.unhandledMessage(msg);
- if (isQuit(msg)) {
- transitionTo(mQuittingState); //设置状态
- }
- break;
- }
- if (mDbg) {
- Log.d(TAG, "processMsg: " + curStateInfo.state.getName());
- }
- }
- /* Loading the driver */
- private State mDriverUnloadedState = new DriverUnloadedState();
- /* Driver load/unload failed */
- private State mDriverFailedState = new DriverFailedState();
- /* Driver loading */
- private State mDriverLoadingState = new DriverLoadingState();
- /* Driver loaded */
- private State mDriverLoadedState = new DriverLoadedState();
- @Override
- public boolean processMessage(Message message) {
- if (DBG) log(getName() + message.toString() + "\n");
- switch (message.what) {
- case CMD_LOAD_DRIVER:
- transitionTo(mDriverLoadingState);
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
这说明,在状态是“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中):
- /** @see StateMachine#transitionTo(IState) */
- private final void transitionTo(IState destState) {
- mDestState = (State) destState;
- if (mDbg) Log.d(TAG, "transitionTo: destState=" + mDestState.getName());
- }
由于State是IState的子类,所以这样的参数传递进去没有问题,mDestState目标状态变成了mDriverLoadingState,然后是performTransitions()(还是在父类StateMachine中):
- /**
- * Do any transitions
- */
- private void performTransitions() {
- /**
- * If transitionTo has been called, exit and then enter
- * the appropriate states. We loop on this to allow
- * enter and exit methods to use transitionTo.
- */
- State destState = null;
- while (mDestState != null) { //即transitionTo设置的新状态 mDriverLoadingState
- if (mDbg) Log.d(TAG, "handleMessage: new destination call exit");
- /**
- * Save mDestState locally and set to null
- * to know if enter/exit use transitionTo.
- */
- destState = mDestState;
- mDestState = null;
- /**
- * Determine the states to exit and enter and return the
- * common ancestor state of the enter/exit states. Then
- * invoke the exit methods then the enter methods.
- */
- StateInfo commonStateInfo = setupTempStateStackWithStatesToEnter(destState); //将状态装入临时队列
- invokeExitMethods(commonStateInfo); //将该状态之前的所有状态全部退出
- int stateStackEnteringIndex = moveTempStateStackToStateStack(); //把临时队列合并至标准队列,并且返回界限值stateStaclEnteringIndex
- invokeEnterMethods(stateStackEnteringIndex); //遍历执行自界限值到队列顶部的所有状态的enter方法,如下图所示:
- /*
- |-------------|
- High| stack_x | mStateStackTopIndex
- |-------------|
- | ..... |
- |-------------|
- | stack_y | stateStackEnteringIndex 以上都是从Temp临时队列合并来的
- |-------------|
- | ..... |
- |-------------|
- Low | stack_1 |
- |-------------|
- */
- /**
- * Since we have transitioned to a new state we need to have
- * any deferred messages moved to the front of the message queue
- * so they will be processed before any other messages in the
- * message queue.
- */ moveDeferredMessageAtFrontOfQueue(); //将所有延迟消息再次发送到队列顶部,随后清除延迟消息队列。 }
- /**
- * After processing all transitions check and
- * see if the last transition was to quit or halt.
- */
- if (destState != null) { //以下检查状态是否是需求退出或挂起的,是则进行相应处理
- if (destState == mQuittingState) {
- cleanupAfterQuitting();
- } else if (destState == mHaltingState) {
- /**
- * Call halting() if we've transitioned to the halting
- * state. All subsequent messages will be processed in
- * in the halting state which invokes haltedProcessMessage(msg);
- */
- mSm.halting();
- }
- }
- }
看了好多子函数,有点晕晕的。看得出来这个performTransitions()是对所有状态进行处理的关键节点,可能同一时间会受到很多Message,而这些Message所携带的不同状态会被加入到一个临时队列中,然后会将标准队列顶端到此状态之前的所有状态都退出(也就是触发exit()),并设置为非活跃,然后剔除。之后会将临时队列合并入标准队列,取得一个界限值,从界限值到队列顶端依次激活(触发enter())。其实在sendMessage的同时,还有一种消息处理方式就是deferMessage,是对消息的延迟发送,最终会将消息加入到一个延迟消息队列mDeferredMessages中,每次的performTransitions()都会对延迟消息队列进行重新发送并且清空它的队列。最后,还会检测一下是否有特殊的状态需要处理,如退出和挂起。
回到正题
应该关注一下mDriverLoadingState了,前边看到这是一个DriverLoadingState(),enter()的主要内容是一个工作线程:
- new Thread(new Runnable() {
- public void run() {
- mWakeLock.acquire(); //整个过程需要wakelock保护
- //enabling state
- switch(message.arg1) {
- case WIFI_STATE_ENABLING: //打开WIFI
- setWifiState(WIFI_STATE_ENABLING);
- break;
- case WIFI_AP_STATE_ENABLING: //打开WIFI AP
- setWifiApState(WIFI_AP_STATE_ENABLING);
- break;
- }
- if(mWifiNative.loadDriver()) { //加载Wifi驱动,WifiNative.java --> core/jni/android_net_wifi_Wifi.cpp --> hardware/libhardware_legacy/wifi.c 就是insmod xxx.ko,也许带参数blablabla
- if (DBG) log("Driver load successful");
- sendMessage(CMD_LOAD_DRIVER_SUCCESS);
- } else {
- loge("Failed to load driver!");
- switch(message.arg1) {
- case WIFI_STATE_ENABLING:
- setWifiState(WIFI_STATE_UNKNOWN);
- break;
- case WIFI_AP_STATE_ENABLING:
- setWifiApState(WIFI_AP_STATE_FAILED);
- break;
- }
- sendMessage(CMD_LOAD_DRIVER_FAILURE);
- }
- mWakeLock.release();
- }
- }).start();
- }
而这里可以快速的复习一下前边的流程,加载成功后会sendMessage(CMD_LOAD_DRIVER_SUCCESS),失败了就会发送CMD_LOAD_DRIVER_FAILURE。当前的状态就是mDriverLoadingState,所以是DriverLoadingState的processMessage来处理这两个消息了:
- @Override
- public boolean processMessage(Message message) {
- if (DBG) log(getName() + message.toString() + "\n");
- switch (message.what) {
- case CMD_LOAD_DRIVER_SUCCESS:
- transitionTo(mDriverLoadedState);
- break;
- case CMD_LOAD_DRIVER_FAILURE:
- transitionTo(mDriverFailedState);
- break;
- case CMD_LOAD_DRIVER:
- case CMD_UNLOAD_DRIVER:
- case CMD_START_SUPPLICANT:
- case CMD_STOP_SUPPLICANT:
- case CMD_START_AP:
- case CMD_STOP_AP:
- case CMD_START_DRIVER:
- case CMD_STOP_DRIVER:
- case CMD_SET_SCAN_MODE:
- case CMD_SET_SCAN_TYPE:
- case CMD_SET_COUNTRY_CODE:
- case CMD_SET_FREQUENCY_BAND:
- case CMD_START_PACKET_FILTERING:
- case CMD_STOP_PACKET_FILTERING:
- deferMessage(message);
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
由此可见,加载成功后状态就变为mDriverLoadedState,失败了状态就是mDriverFailedState。回到DriverLoadingState的enter,setWifiApState:
- private void setWifiApState(int wifiApState) {
- final int previousWifiApState = mWifiApState.get();
- try {
- if (wifiApState == WIFI_AP_STATE_ENABLED) { //WIFI AP已经打开,则电池状态开始记录Wifi相关
- mBatteryStats.noteWifiOn();
- } else if (wifiApState == WIFI_AP_STATE_DISABLED) { //WIFI AP已经关闭,则电池状态对WIFI的记录关闭
- mBatteryStats.noteWifiOff();
- }
- } catch (RemoteException e) {
- loge("Failed to note battery stats in wifi");
- }
- // Update state
- mWifiApState.set(wifiApState); //设置WIFI AP的状态,原子状态
- if (DBG) log("setWifiApState: " + syncGetWifiApStateByName());
- //将状态消息发送至WifiManager进行进一步处理。终于脱离了状态机,回到WifiManager了。
- final Intent intent = new Intent(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
- intent.putExtra(WifiManager.EXTRA_WIFI_AP_STATE, wifiApState);
- intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, previousWifiApState);
- mContext.sendStickyBroadcast(intent);
- }
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的处理方法:
- case CMD_START_AP:
- transitionTo(mSoftApStartingState);
- break;
新的状态,mSoftApStartingState:
- /* Soft ap is starting up */
- private State mSoftApStartingState = new SoftApStartingState();
- @Override
- public void enter() {
- if (DBG) log(getName() + "\n");
- EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
- final Message message = getCurrentMessage();
- if (message.what == CMD_START_AP) { //如果进入这个状态而不是打开AP,那么就直接抛出runtime异常,一般来说就是重启了。又一次验证了:不以结婚为目的的谈恋爱都是耍流氓。
- final WifiConfiguration config = (WifiConfiguration) message.obj;
- if (config == null) {
- mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG);
- } else {
- mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);
- startSoftApWithConfig(config);
- }
- } else {
- throw new RuntimeException("Illegal transition to SoftApStartingState: " + message);
- }
- }
OK, config为NULL,又是一个Message:
- mWifiApConfigChannel.sendMessage(CMD_REQUEST_AP_CONFIG);
在WifiStateMachine构造的时候对mWifiApConfigChannel设置了handler:
- mWifiApConfigChannel.connectSync(mContext, getHandler(), wifiApConfigStore.getMessenger());
WifiApConfigStore.java
CMD_REQUEST_AP_CONFIG的消息处理是在WifiApConfigStore中处理的:
- class DefaultState extends State {
- public boolean processMessage(Message message) {
- switch (message.what) {
- case WifiStateMachine.CMD_SET_AP_CONFIG:
- case WifiStateMachine.CMD_SET_AP_CONFIG_COMPLETED:
- Log.e(TAG, "Unexpected message: " + message);
- break;
- case WifiStateMachine.CMD_REQUEST_AP_CONFIG:
- mReplyChannel.replyToMessage(message,
- WifiStateMachine.CMD_RESPONSE_AP_CONFIG, mWifiApConfig);
- break;
- default:
- Log.e(TAG, "Failed to handle " + message);
- break;
- }
- return HANDLED;
- }
- }
当前WIFI状态机状态为SoftApStartingState,所以回复消息在这里处理:
- @Override
- public boolean processMessage(Message message) {
- if (DBG) log(getName() + message.toString() + "\n");
- switch(message.what) {
- case CMD_LOAD_DRIVER:
- case CMD_UNLOAD_DRIVER:
- case CMD_START_SUPPLICANT:
- case CMD_STOP_SUPPLICANT:
- case CMD_START_AP:
- case CMD_STOP_AP:
- case CMD_START_DRIVER:
- case CMD_STOP_DRIVER:
- case CMD_SET_SCAN_MODE:
- case CMD_SET_SCAN_TYPE:
- case CMD_SET_COUNTRY_CODE:
- case CMD_SET_FREQUENCY_BAND:
- case CMD_START_PACKET_FILTERING:
- case CMD_STOP_PACKET_FILTERING:
- case CMD_TETHER_STATE_CHANGE:
- deferMessage(message);
- break;
- case WifiStateMachine.CMD_RESPONSE_AP_CONFIG:
- WifiConfiguration config = (WifiConfiguration) message.obj; //设置文件就是WifiApConfigStore中的mWifiApConfig
- if (config != null) {
- startSoftApWithConfig(config); //如果配置文件存在就继续开启AP
- } else {
- loge("Softap config is null!"); //如果配置文件为空则开启失败,发送个消息CMD_START_AP_FAILURE,还是在本状态中处理
- sendMessage(CMD_START_AP_FAILURE);
- }
- break;
- case CMD_START_AP_SUCCESS:
- setWifiApState(WIFI_AP_STATE_ENABLED);
- transitionTo(mSoftApStartedState);
- break;
- case CMD_START_AP_FAILURE:
- // initiate driver unload
- sendMessage(obtainMessage(CMD_UNLOAD_DRIVER, WIFI_AP_STATE_FAILED, 0)); //卸载驱动,并更改状态为AP开启失败
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
配置文件
这里的配置文件是通过WifiManager的setWifiApConfiguration接口生成的:frameworks/base/wifi/java/android/net/wifi/WifiManager.java
- /**
- * Sets the Wi-Fi AP Configuration.
- * @return {@code true} if the operation succeeded, {@code false} otherwise
- *
- * @hide Dont open yet
- */
- public boolean setWifiApConfiguration(WifiConfiguration wifiConfig) {
- try {
- mService.setWifiApConfiguration(wifiConfig);
- return true;
- } catch (RemoteException e) {
- return false;
- }
- }
mService为IWifiManager,该接口定义如下:
- void setWifiApConfiguration(in WifiConfiguration wifiConfig);
而实现为WifiService
- public class WifiService extends IWifiManager.Stub
- /**
- * see {@link WifiManager#setWifiApConfiguration(WifiConfiguration)}
- * @param wifiConfig WifiConfiguration details for soft access point
- */
- public void setWifiApConfiguration(WifiConfiguration wifiConfig) {
- enforceChangePermission();
- if (wifiConfig == null)
- return;
- mWifiStateMachine.setWifiApConfiguration(wifiConfig);
- }
真是的实现有抛给了WifiStateMachine:
- public void setWifiApConfiguration(WifiConfiguration config) {
- mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config);
- }
消息交给WifiApConfigStore处理,而
- WifiApConfigStore(Context context, Handler target) {
- super(TAG, target.getLooper());
- mContext = context;
- addState(mDefaultState);
- addState(mInactiveState, mDefaultState);
- addState(mActiveState, mDefaultState);
- setInitialState(mInactiveState);
- }
WifiApConfigStore在构造的时候分mDefaultState分配了两个子状态mInactiveState, mActiveState, 初始化状态为mInactiveState。
- class InactiveState extends State {
- public boolean processMessage(Message message) {
- switch (message.what) {
- case WifiStateMachine.CMD_SET_AP_CONFIG:
- mWifiApConfig = (WifiConfiguration) message.obj;
- transitionTo(mActiveState); //触发ActiveState.enter()
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
- }
- class ActiveState extends State {
- public void enter() {
- new Thread(new Runnable() {
- public void run() {
- writeApConfiguration(mWifiApConfig);
- sendMessage(WifiStateMachine.CMD_SET_AP_CONFIG_COMPLETED);
- }
- }).start();
- }
writeApConfiguration实现:
- private void writeApConfiguration(final WifiConfiguration config) {
- DataOutputStream out = null;
- try {
- out = new DataOutputStream(new BufferedOutputStream(
- new FileOutputStream(AP_CONFIG_FILE)));
- out.writeInt(AP_CONFIG_FILE_VERSION);
- out.writeUTF(config.SSID);
- int authType = config.getAuthType();
- out.writeInt(authType);
- if(authType != KeyMgmt.NONE) {
- out.writeUTF(config.preSharedKey);
- }
- } catch (IOException e) {
- Log.e(TAG, "Error writing hotspot configuration" + e);
- } finally {
- if (out != null) {
- try {
- out.close();
- } catch (IOException e) {}
- }
- }
- }
默认文件路径即为/misc/wifi/softap.conf,写好配置文件后发送WifiStateMachine.CMD_SET_AP_CONFIG_COMPLETED,自己发自己收了:
- public boolean processMessage(Message message) {
- switch (message.what) {
- //TODO: have feedback to the user when we do this
- //to indicate the write is currently in progress
- case WifiStateMachine.CMD_SET_AP_CONFIG:
- deferMessage(message);
- break;
- case WifiStateMachine.CMD_SET_AP_CONFIG_COMPLETED: //修改完后切换状态到InactiveState
- transitionTo(mInactiveState);
- break;
- default:
- return NOT_HANDLED;
- }
- return HANDLED;
- }
这样配置文件就配置完了,结果是保存在mWifiConfig中的。
带着配置文件开启AP
- /* Current design is to not set the config on a running hostapd but instead
- * stop and start tethering when user changes config on a running access point
- *
- * TODO: Add control channel setup through hostapd that allows changing config
- * on a running daemon
- */
- private void startSoftApWithConfig(final WifiConfiguration config) {
- // start hostapd on a seperate thread
- new Thread(new Runnable() {
- public void run() {
- try {
- mNwService.startAccessPoint(config, mInterfaceName, SOFTAP_IFACE);
- } catch (Exception e) {
- loge("Exception in softap start " + e);
- try {
- mNwService.stopAccessPoint(mInterfaceName);
- mNwService.startAccessPoint(config, mInterfaceName, SOFTAP_IFACE);
- } catch (Exception e1) {
- loge("Exception in softap re-start " + e1);
- sendMessage(CMD_START_AP_FAILURE);
- return;
- }
- }
- if (DBG) log("Soft AP start successful");
- sendMessage(CMD_START_AP_SUCCESS);
- }
- }).start();
- }
逻辑就是尝试开启,如果发生错误就尝试重启,如果再错误就承认失败,发送失败状态,如果没错误就发送成功的消息。关键在mNwService的startAccessPoint方法中。
- WifiService(Context context) {
- mContext = context;
- mInterfaceName = SystemProperties.get("wifi.interface", "wlan0");
- mWifiStateMachine = new WifiStateMachine(mContext, mInterfaceName);
- mWifiStateMachine.enableRssiPolling(true);
- ...
- }
可见,这个mInterfaceName由prop wifi.interface控制,如我们经常能在build.prop中看到wifi.interface=eth0/wlan0等,如果没有会默认给wlan0。
- @Override
- public void startAccessPoint(
- WifiConfiguration wifiConfig, String wlanIface, String softapIface) {
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- try {
- Resources resources = mContext.getResources();
- if (resources.getBoolean(com.android.internal.R.bool.config_wifiApFirmwareReload))
- wifiFirmwareReload(wlanIface, "AP");
- if (resources.getBoolean(com.android.internal.R.bool.config_wifiApStartInterface))
- mConnector.execute("softap", "start", wlanIface);
- if (wifiConfig == null) {
- mConnector.execute("softap", "set", wlanIface, softapIface);
- } else {
- mConnector.execute("softap", "set", wlanIface, softapIface, wifiConfig.SSID,
- getSecurityType(wifiConfig), wifiConfig.preSharedKey);
- }
- mConnector.execute("softap", "startap");
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
- }
- }
config_wifiApFirmwareReload、config_wifiApStartInterface都是可以用户自定义的xml配置接口,默认在frameworks/base/core/res/res/values/config.xml中,默认如:
- <!-- Boolean indicating whether Softap requires reloading AP firmware -->
- <bool name="config_wifiApFirmwareReload">true</bool>
- <!-- Boolean indicating whether the start command should be called on the wireless interface
- when starting the SoftAp -->
- <bool name="config_wifiApStartInterface">false</bool>
想关联的几个函数有:
- private static String getSecurityType(WifiConfiguration wifiConfig) { //获取网络安全类型
- switch (wifiConfig.getAuthType()) {
- case KeyMgmt.WPA_PSK:
- return "wpa-psk";
- case KeyMgmt.WPA2_PSK:
- return "wpa2-psk";
- default:
- return "open";
- }
- }
- /* @param mode can be "AP", "STA" or "P2P" */
- @Override
- public void wifiFirmwareReload(String wlanIface, String mode) { //根据不同模式装在不同的固件(如果有需要的话)
- mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- try {
- mConnector.execute("softap", "fwreload", wlanIface, mode);
- } catch (NativeDaemonConnectorException e) {
- throw e.rethrowAsParcelableException();
- }
- }
通过以上不难看出,最终都是通过mConnector.execute来执行命令。
- /**
- * Constructs a new NetworkManagementService instance
- *
- * @param context Binder context for this service
- */
- private NetworkManagementService(Context context) {
- mContext = context;
- if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
- return;
- }
- mConnector = new NativeDaemonConnector(
- new NetdCallbackReceiver(), "netd", 10, NETD_TAG, 160);
- mThread = new Thread(mConnector, NETD_TAG);
- // Add ourself to the Watchdog monitors.
- Watchdog.getInstance().addMonitor(this);
- }
mConnector是在构造时生成的NativeDaemonConnector对象,查看一下NativeDaemonConnector的构造过程(frameworks/base/services/java/com/android/server/NativeDaemonConnector.java):
- NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket,
- int responseQueueSize, String logTag, int maxLogSize) {
- mCallbacks = callbacks;
- mSocket = socket;
- mResponseQueue = new ResponseQueue(responseQueueSize);
- mSequenceNumber = new AtomicInteger(0);
- TAG = logTag != null ? logTag : "NativeDaemonConnector";
- mLocalLog = new LocalLog(maxLogSize);
- }
1.分别在handleMessage和listenToSocket的时候调用回调对象的onEvent和onDaemonConnected方法。而监听socket的服务被创建后就已经开出一个线程始终监听了。在这里为new NetdCallbackReceiver();
a.构造
- mConnector.execute("softap", "fwreload", wlanIface, mode);
- mConnector.execute("softap", "set", wlanIface, softapIface, wifiConfig.SSID,
- getSecurityType(wifiConfig), wifiConfig.preSharedKey);
逐个分析一下:
固件重载
- public NativeDaemonEvent[] execute(int timeout, String cmd, Object... args)
- throws NativeDaemonConnectorException {
- final ArrayList<NativeDaemonEvent> events = Lists.newArrayList();
- final int sequenceNumber = mSequenceNumber.incrementAndGet(); //命令计数器加一并返回
- final StringBuilder cmdBuilder =
- new StringBuilder(Integer.toString(sequenceNumber)).append(' ');
- final long startTime = SystemClock.elapsedRealtime(); //返回的是自从系统启动到当前的时间
- makeCommand(cmdBuilder, cmd, args); //将所有参数整合成一条命令,放置在cmdBuilder中
- final String logCmd = cmdBuilder.toString(); /* includes cmdNum, cmd, args */
- log("SND -> {" + logCmd + "}");
- cmdBuilder.append('\0'); //给字符串来个尾巴,然后化作真正的字符串sentCmd
- final String sentCmd = cmdBuilder.toString(); /* logCmd + \0 */
- synchronized (mDaemonLock) {
- if (mOutputStream == null) { //mOutputStraem是netd的输出通道
- throw new NativeDaemonConnectorException("missing output stream");
- } else {
- try {
- mOutputStream.write(sentCmd.getBytes(Charsets.UTF_8)); //将命令发送出去 netd socket
- } catch (IOException e) {
- throw new NativeDaemonConnectorException("problem sending command", e);
- }
- }
- }
- NativeDaemonEvent event = null;
- do {
- event = mResponseQueue.remove(sequenceNumber, timeout, sentCmd); //从命令队列中删除已经发送出去的命令
- if (event == null) {
- loge("timed-out waiting for response to " + logCmd);
- throw new NativeDaemonFailureException(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 new NativeDaemonArgumentException(logCmd, event);
- }
- if (event.isClassServerError()) {
- throw new NativeDaemonFailureException(logCmd, event);
- }
- return events.toArray(new NativeDaemonEvent[events.size()]);
- }
现在看来,所有命令都是通过netd socket发送出去。但是这个socket是谁来接收呢?
这里通过一个NativeDaemonConnector的实例mConnector 调用c++程序 具体的实现我是没看懂 但是知道最后实际调用的函数, 想深入了解可以找一些其他的资料看
实际调用到了 "./system/netd/CommandListener.cpp" 中的CommandListener::SoftapCmd::runCommand
- int CommandListener::SoftapCmd::runCommand(SocketClient *cli,
- int argc, char **argv) {
- int rc = 0, flag = 0;
- char *retbuf = NULL;
- if (argc < 2) {
- cli->sendMsg(ResponseCode::CommandSyntaxError, "Softap Missing argument", false);
- return 0;
- }
- if (!strcmp(argv[1], "startap")) {
- rc = sSoftapCtrl->startSoftap();
- } else if (!strcmp(argv[1], "stopap")) {
- rc = sSoftapCtrl->stopSoftap();
- } else if (!strcmp(argv[1], "fwreload")) {
- rc = sSoftapCtrl->fwReloadSoftap(argc, argv);
- } else if (!strcmp(argv[1], "clients")) {
- rc = sSoftapCtrl->clientsSoftap(&retbuf);
- if (!rc) {
- cli->sendMsg(ResponseCode::CommandOkay, retbuf, false);
- free(retbuf);
- return 0;
- }
- } else if (!strcmp(argv[1], "status")) {
- asprintf(&retbuf, "Softap service %s",
- (sSoftapCtrl->isSoftapStarted() ? "started" : "stopped"));
- cli->sendMsg(ResponseCode::SoftapStatusResult, retbuf, false);
- free(retbuf);
- return 0;
- } else if (!strcmp(argv[1], "set")) {
- rc = sSoftapCtrl->setSoftap(argc, argv);
- } else {
- cli->sendMsg(ResponseCode::CommandSyntaxError, "Softap Unknown cmd", false);
- return 0;
- }
- if (!rc) {
- cli->sendMsg(ResponseCode::CommandOkay, "Softap operation succeeded", false);
- } else {
- cli->sendMsg(ResponseCode::OperationFailed, "Softap operation failed", true);
- }
- return 0;
- }
首先是"set“ 命令, 调用到c = sSoftapCtrl->setSoftap(argc, argv); 来配置网络
配置即将所有上层的网络设置写到HOSTAPD_CONF_FILE[] = "/data/misc/wifi/hostapd.conf" 中
("system/netd/SoftapController.cpp")
- /*
- * Arguments:
- * argv[2] - wlan interface
- * argv[3] - SSID
- * argv[4] - Security
- * argv[5] - Key
- * argv[6] - Channel
- * argv[7] - Preamble
- * argv[8] - Max SCB
- */
- int SoftapController::setSoftap(int argc, char *argv[]) {
- char psk_str[2*SHA256_DIGEST_LENGTH+1];
- int ret = 0, i = 0, fd;
- char *ssid, *iface;
- /* ..... */
- iface = argv[2];
- char *wbuf = NULL;
- char *fbuf = NULL;
- if (argc > 3) {
- ssid = argv[3];
- } else {
- ssid = (char *)"AndroidAP";
- }
- if (argc > 4) {
- if (!strcmp(argv[4], "wpa-psk")) {
- generatePsk(ssid, argv[5], psk_str);
- asprintf(&fbuf, "%swpa=1\nwpa_pairwise=TKIP CCMP\nwpa_psk=%s\n", wbuf, psk_str);
- } else if (!strcmp(argv[4], "wpa2-psk")) {
- generatePsk(ssid, argv[5], psk_str);
- asprintf(&fbuf, "%swpa=2\nrsn_pairwise=CCMP\nwpa_psk=%s\n", wbuf, psk_str);
- } else if (!strcmp(argv[4], "open")) {
- asprintf(&fbuf, "%s", wbuf);
- }
- } else {
- asprintf(&fbuf, "%s", wbuf);
- }
- fd = open(HOSTAPD_CONF_FILE, O_CREAT | O_TRUNC | O_WRONLY | O_NOFOLLOW, 0660);
- /*............*/
- if (write(fd, fbuf, strlen(fbuf)) < 0) {
- ALOGE("Cannot write to \"%s\": %s", HOSTAPD_CONF_FILE, strerror(errno));
- ret = -1;
- }
- free(wbuf);
- free(fbuf);
- /* Note: apparently open can fail to set permissions correctly at times */
- // .......
- }
然后是"startap"命令调用rc = sSoftapCtrl->startSoftap(); 真正开启Softap
- int SoftapController::startSoftap() {
- pid_t pid = 1;
- int ret = 0;
- if (mPid) {
- ALOGE("Softap already started");
- return 0;
- }
- if (mSock < 0) {
- ALOGE("Softap startap - failed to open socket");
- return -1;
- }
- if ((pid = fork()) < 0) {
- ALOGE("fork failed (%s)", strerror(errno));
- return -1;
- }
- if (!pid) {
- ensure_entropy_file_exists();
- if (execl("/system/bin/hostapd", "/system/bin/hostapd",
- "-e", WIFI_ENTROPY_FILE,
- HOSTAPD_CONF_FILE, (char *) NULL)) {
- ALOGE("execl failed (%s)", strerror(errno));
- }
- ALOGE("Should never get here!");
- return -1;
- } else {
- mPid = pid;
- ALOGD("Softap startap - Ok");
- usleep(AP_BSS_START_DELAY);
- }
- return ret;
- }
在startSoftap函数中调用了
execl("/system/bin/hostapd", "/system/bin/hostapd", "-e", WIFI_ENTROPY_FILE, HOSTAPD_CONF_FILE, (char *) NULL)
这里hostapd就是softap的deamon 程序 类似于wifi的的wpa_supplicant
至此所有wifi子系统从界面打开softap 到如何运行调用到deamon程序打开Softap的流程就是这样的
之后会介绍到Setting 界面"Portable Wi-Fi"的开启 以及 Hostapd 的一些东东