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方法的复写,所以实现是使用父类的实现:
/** * 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); }
/** * 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"); }
/** * 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()); } }在WifiStateMachine中有很多状态,截取几个来看:
/* 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();以上4个都是关于Wifi驱动加载与卸载的相关状态,每一个都有复写自己的processMessage方法,比如DriverUnloadedState():
@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热点的时候,驱动就是卸载的(默认状态),那么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()); }
/** * 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(); } } }
应该关注一下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(); }
@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; } }
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); }
好的,分析了这么久,只是有一条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;
/* 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());
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; } }
@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; }
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; } }
void setWifiApConfiguration(in WifiConfiguration wifiConfig);
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); }
public void setWifiApConfiguration(WifiConfiguration config) { mWifiApConfigChannel.sendMessage(CMD_SET_AP_CONFIG, config); }
WifiApConfigStore(Context context, Handler target) { super(TAG, target.getLooper()); mContext = context; addState(mDefaultState); addState(mInactiveState, mDefaultState); addState(mActiveState, mDefaultState); setInitialState(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(); }
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) {} } } }
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; }
/* 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(); }
WifiService(Context context) { mContext = context; mInterfaceName = SystemProperties.get("wifi.interface", "wlan0"); mWifiStateMachine = new WifiStateMachine(mContext, mInterfaceName); mWifiStateMachine.enableRssiPolling(true); ... }
@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(); } }
<!-- 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(); } }
/** * 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); }
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); }
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()]); }