Android -- Wifi启动流程分析


Android网络各个模式中,Wifi应该是目前最常用的一种网络方式了;下面就简单介绍下Android中Wifi的启动流程。
当我在Setting菜单里点击打开Wifi时,调用的入口函数是WifiManager::setWifiEnabled(boolean enabled):
    /**     * Enable or disable Wi-Fi.     * @param enabled {@code true} to enable, {@code false} to disable.     * @return {@code true} if the operation succeeds (or if the existing state     *         is the same as the requested state).     */    public boolean setWifiEnabled(boolean enabled) {        try {            return mService.setWifiEnabled(enabled);        } catch (RemoteException e) {            return false;        }    }
通过AIDL方式,在Android6.0中,实际调用的是WifiServiceImpl::setWifiEnabled(boolean enable):
    /**     * see {@link android.net.wifi.WifiManager#setWifiEnabled(boolean)}     * @param enable {@code true} to enable, {@code false} to disable.     * @return {@code true} if the enable/disable operation was     *         started or is already in the queue.     */    public synchronized boolean setWifiEnabled(boolean enable) {        enforceChangePermission();        Slog.d(TAG, "setWifiEnabled: " + enable + " pid=" + Binder.getCallingPid()                    + ", uid=" + Binder.getCallingUid());        if (DBG) {            Slog.e(TAG, "Invoking mWifiStateMachine.setWifiEnabled\n");        }        /*        * Caller might not have WRITE_SECURE_SETTINGS,        * only CHANGE_WIFI_STATE is enforced        */        long ident = Binder.clearCallingIdentity();        try {            if (! mSettingsStore.handleWifiToggled(enable)) {                // Nothing to do if wifi cannot be toggled                return true;            }        } finally {            Binder.restoreCallingIdentity(ident);        }        mWifiController.sendMessage(CMD_WIFI_TOGGLED);        return true;    }
从代码可以看出,这里主要的操作是将wifi是否enable的状态存入数据库、向WiFiController发送了CMD_WIFI_TOGGLED消息。
WifiController实际上是一个状态机,相比WifiStateMachine,它的状态较少,结构也比较简单。WifiController的定义及构造函数:
class WifiController extends StateMachine {   ...   WifiController(Context context, WifiServiceImpl service, Looper looper) {        super(TAG, looper);        ...          addState(mDefaultState);            addState(mApStaDisabledState, mDefaultState);            addState(mStaEnabledState, mDefaultState);                addState(mDeviceActiveState, mStaEnabledState);                addState(mDeviceInactiveState, mStaEnabledState);                    addState(mScanOnlyLockHeldState, mDeviceInactiveState);                    addState(mFullLockHeldState, mDeviceInactiveState);                    addState(mFullHighPerfLockHeldState, mDeviceInactiveState);                    addState(mNoLockHeldState, mDeviceInactiveState);            addState(mStaDisabledWithScanState, mDefaultState);            addState(mApEnabledState, mDefaultState);            addState(mEcmState, mDefaultState);        boolean isAirplaneModeOn = mSettingsStore.isAirplaneModeOn();        boolean isWifiEnabled = mSettingsStore.isWifiToggleEnabled();        boolean isScanningAlwaysAvailable = mSettingsStore.isScanAlwaysAvailable();        log("isAirplaneModeOn = " + isAirplaneModeOn +                ", isWifiEnabled = " + isWifiEnabled +                ", isScanningAvailable = " + isScanningAlwaysAvailable);        if (isScanningAlwaysAvailable) {            setInitialState(mStaDisabledWithScanState);        } else {            setInitialState(mApStaDisabledState);        }        ...    }    ...}
WifiController状态机的创建、开启工作在WifiServiceImpl中完成:
    public WifiServiceImpl(Context context) {        mContext = context;        mInterfaceName =  SystemProperties.get("wifi.interface", "wlan0");        mTrafficPoller = new WifiTrafficPoller(mContext, mInterfaceName);        mWifiStateMachine = new WifiStateMachine(mContext, mInterfaceName, mTrafficPoller);        mWifiStateMachine.enableRssiPolling(true);        mBatteryStats = BatteryStatsService.getService();        mPowerManager = context.getSystemService(PowerManager.class);        mAppOps = (AppOpsManager)context.getSystemService(Context.APP_OPS_SERVICE);        mUserManager = UserManager.get(mContext);        mNotificationController = new WifiNotificationController(mContext, mWifiStateMachine);        mSettingsStore = new WifiSettingsStore(mContext);        HandlerThread wifiThread = new HandlerThread("WifiService");        wifiThread.start();        mClientHandler = new ClientHandler(wifiThread.getLooper());        mWifiStateMachineHandler = new WifiStateMachineHandler(wifiThread.getLooper());        mWifiController = new WifiController(mContext, this, wifiThread.getLooper());    }
WifiController中的各状态之间的关系如图: Android -- Wifi启动流程分析_第1张图片
WifiControlle状态机的初始状态由一些配置信息决定。当ApStaDisabledState为初始状态时,看对CMD_WIFI_TOGGLED消息的处理:
    class ApStaDisabledState extends State {        ...        @Override        public boolean processMessage(Message msg) {            switch (msg.what) {                case CMD_WIFI_TOGGLED:                case CMD_AIRPLANE_TOGGLED:                    if (mSettingsStore.isWifiToggleEnabled()) {                        if (doDeferEnable(msg)) {                            if (mHaveDeferredEnable) {                                //  have 2 toggles now, inc serial number an ignore both                                mDeferredEnableSerialNumber++;                            }                            mHaveDeferredEnable = !mHaveDeferredEnable;                            break;                        }                        if (mDeviceIdle == false) {                            transitionTo(mDeviceActiveState);                        } else {                            checkLocksAndTransitionWhenDeviceIdle();                        }                    } else if (mSettingsStore.isScanAlwaysAvailable()) {                        transitionTo(mStaDisabledWithScanState);                    }                    break;                case CMD_SCAN_ALWAYS_MODE_CHANGED:                    if (mSettingsStore.isScanAlwaysAvailable()) {                        transitionTo(mStaDisabledWithScanState);                    }                    break;                ...                default:                    return NOT_HANDLED;            }            return HANDLED;        }        private boolean doDeferEnable(Message msg) {            long delaySoFar = SystemClock.elapsedRealtime() - mDisabledTimestamp;            if (delaySoFar >= mReEnableDelayMillis) {                return false;            }            log("WifiController msg " + msg + " deferred for " +                    (mReEnableDelayMillis - delaySoFar) + "ms");            // need to defer this action.            Message deferredMsg = obtainMessage(CMD_DEFERRED_TOGGLE);            deferredMsg.obj = Message.obtain(msg);            deferredMsg.arg1 = ++mDeferredEnableSerialNumber;            sendMessageDelayed(deferredMsg, mReEnableDelayMillis - delaySoFar + DEFER_MARGIN_MS);            return true;        }    }
调试过程中发现,ApStaDisabledState会同时忽略两个时间间隔小于500ms的 CMD_WIFI_TOGGLED消息,接着转换到DeviceActiveState状态。StaEnabledState是它的父状态,由StateMachine的知识可知,转换到该状态时,会依次调用父、子状态的enter()函数。我们看两个状态的enter()函数:
    class StaEnabledState extends State {        @Override        public void enter() {            mWifiStateMachine.setSupplicantRunning(true);        }        ...        }    }    /* Parent: StaEnabledState */    class DeviceActiveState extends State {        @Override        public void enter() {            mWifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE);            mWifiStateMachine.setDriverStart(true);            mWifiStateMachine.setHighPerfModeEnabled(false);//此处分析忽略,关系不大        }        ...    }
这里依次会向WifiStateMachine发送三个消息,最后一个消息这里忽略:
  1. WifiStateMachine.setSupplicantRunning(true):发送CMD_START_SUPPLICANT消息
  2. WifiStateMachine.setOperationalMode(WifiStateMachine.CONNECT_MODE):发送CMD_SET_OPERATIONAL_MODE消息,参数是CONNECT_MODE
  3. WifiStateMachine.setDriverStart(true):发送CMD_START_DRIVER消息

在此之前,我们先看下第二点中的参数CONNECT_MODE的含义。在WifiStateMachine中,已经有了如下定义:
    /* Wifi state machine modes of operation */    /* CONNECT_MODE - connect to any 'known' AP when it becomes available */    public static final int CONNECT_MODE = 1;    /* SCAN_ONLY_MODE - don't connect to any APs; scan, but only while apps hold lock */    public static final int SCAN_ONLY_MODE = 2;    /* SCAN_ONLY_WITH_WIFI_OFF - scan, but don't connect to any APs */    public static final int SCAN_ONLY_WITH_WIFI_OFF_MODE = 3;
    /* 3 operational states for STA operation: CONNECT_MODE, SCAN_ONLY_MODE, SCAN_ONLY_WIFI_OFF_MODE    * In CONNECT_MODE, the STA can scan and connect to an access point    * In SCAN_ONLY_MODE, the STA can only scan for access points    * In SCAN_ONLY_WIFI_OFF_MODE, the STA can only scan for access points with wifi toggle being off    */    private int mOperationalMode = CONNECT_MODE;
可知Wifi状态机一共有三种处理模式:
  1. CONNECT_MODE:该状态下Wifi可以扫描AP,也可以连接AP
  2. SCAN_ONLY_MODE:该状态下Wifi尽可以扫描AP
  3. SCAN_ONLY_WIFI_OFF_MODE:该状态下,Wifi仅可以当Wifi toogle off时允许扫描AP

现在,我们将处理的过程转换到WifiStateMachine。WifiStateMachine是一个复杂的状态机,它维护了Wifi的启动、扫描、连接、断开等多个状态。它运行在自己独有的线程中,拥有自己的消息队列。WifiStateMachine中各状态的关系如图所示: Android -- Wifi启动流程分析_第2张图片

分别来看WifiStateMachine是怎么处理这三个消息的。InitialState首先接收到CMD_START_SUPPLICANT消息并处理:
           case CMD_START_SUPPLICANT:                    if (mWifiNative.loadDriver()) {//加载驱动                        try {                            mNwService.wifiFirmwareReload(mInterfaceName, "STA");//加载wlan固件                        } catch (Exception e) {                            loge("Failed to reload STA firmware " + e);                            // Continue                        }                        try {                            // A runtime crash can leave the interface up and                            // IP addresses configured, and this affects                            // connectivity when supplicant starts up.                            // Ensure interface is down and we have no IP                            // addresses before a supplicant start.                            mNwService.setInterfaceDown(mInterfaceName);                            mNwService.clearInterfaceAddresses(mInterfaceName);                            // Set privacy extensions                            mNwService.setInterfaceIpv6PrivacyExtensions(mInterfaceName, true);                            // IPv6 is enabled only as long as access point is connected since:                            // - IPv6 addresses and routes stick around after disconnection                            // - kernel is unaware when connected and fails to start IPv6 negotiation                            // - kernel can start autoconfiguration when 802.1x is not complete                            mNwService.disableIpv6(mInterfaceName);                        } catch (RemoteException re) {                            loge("Unable to change interface settings: " + re);                        } catch (IllegalStateException ie) {                            loge("Unable to change interface settings: " + ie);                        }                       /* Stop a running supplicant after a runtime restart                        * Avoids issues with drivers that do not handle interface down                        * on a running supplicant properly.                        */                        mWifiMonitor.killSupplicant(mP2pSupported);                        if (WifiNative.startHal() == false) {                            /* starting HAL is optional */                            loge("Failed to start HAL");                        }                        if (mWifiNative.startSupplicant(mP2pSupported)) {//启动wpa_s                            setWifiState(WIFI_STATE_ENABLING);                            if (DBG) log("Supplicant start successful");                            mWifiMonitor.startMonitoring();//建立与wpa_s之间的socket通信连接;开启线程,循环接收来自wpa_s的event,并分发处理                            transitionTo(mSupplicantStartingState);                        } else {                            loge("Failed to start supplicant!");                        }                    } else {                        loge("Failed to load driver");                    }                    break;
主要的处理过程包括:
  1. WifiNative.loadDriver():加载Wifi驱动,实际的实现是在wifi.c中
  2. NetworkManagementService.wifiFirmwareReload(mInterfaceName, "STA"):加载wlan固件
  3. WifiNative.startSupplicant(mP2pSupported):启动wpa_supplicant
  4. WifiMonitor.startMonitoring():在前面一篇博文中已经介绍过WifiMonitor,这一步主要是在WifiMonitor中建立与wpa_supplicant通信的socket通道、创建一个线程接收底层事件并分发处理。这里会创建两个socket通道与wpa_s通信,一个用于下发指令,另一个用于接收事件。成功后WifiMonitor会向WifiStateMachine发送一个代表socket通信建立成功的消息:SUP_CONNECTION_EVENT;收到这个消息就表示Wifi已经启动成功了。
  5. 切换到SupplicantStartingState。

进入SupplicantStartingState后,第一个消息就处理完毕,这时消息队列中按处理先后顺序仍有三个消息:
  1. CMD_SET_OPERATIONAL_MODE,参数是CONNECT_MODE
  2. CMD_START_DRIVER
  3. SUP_CONNECTION_EVENT

切换到SupplicantStartingState,消息队列中的前两个消息在该状态会被延迟处理,直接看SUP_CONNECTION_EVENT的处理过程:
case WifiMonitor.SUP_CONNECTION_EVENT:                    if (DBG) log("Supplicant connection established");                    setWifiState(WIFI_STATE_ENABLED);                    mSupplicantRestartCount = 0;                    /* Reset the supplicant state to indicate the supplicant                     * state is not known at this time */                    mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);                    /* Initialize data structures */                    mLastBssid = null;                    mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;                    mLastSignalLevel = -1;                    mWifiInfo.setMacAddress(mWifiNative.getMacAddress());                    /* set frequency band of operation */                    setFrequencyBand();                    mWifiNative.enableSaveConfig();                    mWifiConfigStore.loadAndEnableAllNetworks();//加载并enable保存的AP                    if (mWifiConfigStore.enableVerboseLogging.get() > 0) {                        enableVerboseLogging(mWifiConfigStore.enableVerboseLogging.get());                    }                    initializeWpsDetails();                    sendSupplicantConnectionChangedBroadcast(true);//广播通知Wpa_s连接已建立,此时已经可以准备连接或扫描Wifi了                    transitionTo(mDriverStartedState);                    break;
这里主要是调用WifiConfigStore.loadAndEnableAllNetworks()加载并enable所有保存在wpa_s中的AP,然后做一些其他的初始化工作,切换到DriverStartedState状态。关注其父状态和自身的enter()函数:
 class SupplicantStartedState extends State {        @Override        public void enter() {            /* Wifi is available as long as we have a connection to supplicant */            mNetworkInfo.setIsAvailable(true);            if (mNetworkAgent != null) mNetworkAgent.sendNetworkInfo(mNetworkInfo);            int defaultInterval = mContext.getResources().getInteger(                    R.integer.config_wifi_supplicant_scan_interval);            mSupplicantScanIntervalMs = Settings.Global.getLong(mContext.getContentResolver(),                    Settings.Global.WIFI_SUPPLICANT_SCAN_INTERVAL_MS,                    defaultInterval);            mWifiNative.setScanInterval((int)mSupplicantScanIntervalMs / 1000);//设置扫描时间间隔            mWifiNative.setExternalSim(true);            /* turn on use of DFS channels */            WifiNative.setDfsFlag(true);            /* set country code */            setCountryCode();            setRandomMacOui();            mWifiNative.enableAutoConnect(false); //可以事先注意该设置        }}  class DriverStartedState extends State {        @Override        public void enter() {            if (PDBG) {                logd("DriverStartedState enter");            }            mWifiLogger.startLogging(mVerboseLoggingLevel > 0);            mIsRunning = true;            mInDelayedStop = false;            mDelayedStopCounter++;            updateBatteryWorkSource(null);            /**             * Enable bluetooth coexistence scan mode when bluetooth connection is active.             * When this mode is on, some of the low-level scan parameters used by the             * driver are changed to reduce interference with bluetooth             */            mWifiNative.setBluetoothCoexistenceScanMode(mBluetoothConnectionActive);            /* initialize network state */            setNetworkDetailedState(DetailedState.DISCONNECTED);            /* Remove any filtering on Multicast v6 at start */            mWifiNative.stopFilteringMulticastV6Packets();            /* Reset Multicast v4 filtering state */            if (mFilteringMulticastV4Packets.get()) {                mWifiNative.startFilteringMulticastV4Packets();            } else {                mWifiNative.stopFilteringMulticastV4Packets();            }            mDhcpActive = false;            if (mOperationalMode != CONNECT_MODE) {                mWifiNative.disconnect();                mWifiConfigStore.disableAllNetworks();                if (mOperationalMode == SCAN_ONLY_WITH_WIFI_OFF_MODE) {                    setWifiState(WIFI_STATE_DISABLED);                }                transitionTo(mScanModeState);            } else {                // Status pulls in the current supplicant state and network connection state                // events over the monitor connection. This helps framework sync up with                // current supplicant state                // TODO: actually check th supplicant status string and make sure the supplicant                // is in disconnecte4d state.                mWifiNative.status();                // Transitioning to Disconnected state will trigger a scan and subsequently AutoJoin                transitionTo(mDisconnectedState);                transitionTo(mDisconnectedState);            }            // We may have missed screen update at boot            if (mScreenBroadcastReceived.get() == false) {                PowerManager powerManager = (PowerManager)mContext.getSystemService(                        Context.POWER_SERVICE);                handleScreenStateChanged(powerManager.isScreenOn());            } else {                // Set the right suspend mode settings                mWifiNative.setSuspendOptimizations(mSuspendOptNeedsDisabled == 0                        && mUserWantsSuspendOpt.get());            }            mWifiNative.setPowerSave(true);            if (mP2pSupported) {                if (mOperationalMode == CONNECT_MODE) {                    mWifiP2pChannel.sendMessage(WifiStateMachine.CMD_ENABLE_P2P);//支持p2p,则会发送命令enbale p2p                } else {                    // P2P statemachine starts in disabled state, and is not enabled until                    // CMD_ENABLE_P2P is sent from here; so, nothing needs to be done to                    // keep it disabled.                }            }            final Intent intent = new Intent(WifiManager.WIFI_SCAN_AVAILABLE);            intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);            intent.putExtra(WifiManager.EXTRA_SCAN_AVAILABLE, WIFI_STATE_ENABLED);            mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);            mHalFeatureSet = WifiNative.getSupportedFeatureSet();            if ((mHalFeatureSet & WifiManager.WIFI_FEATURE_HAL_EPNO)                    == WifiManager.WIFI_FEATURE_HAL_EPNO) {                mHalBasedPnoDriverSupported = true;            }            // Enable link layer stats gathering            mWifiNative.setWifiLinkLayerStats("wlan0", 1);            if (PDBG) {                logd("Driverstarted State enter done, epno=" + mHalBasedPnoDriverSupported                     + " feature=" + mHalFeatureSet);            }        }}
SupplicantStartedState的enter()函数设置了Wifi扫描间隔;DriverStartedState的enter()函数主要进行了一些相关的设置工作,根据配置启动p2p。最后在enter()函数中会将状态切换到DisconnectedState。消息队列中被延迟的两条消息此时会被处理:
  1. CMD_SET_OPERATIONAL_MODE消息在DisconnectedState被处理,将mOperationalMode设置为CONNECT_MODE;Wifi状态机中该字段的默认值也是该值。
  2. CMD_START_DRIVER消息则在DriverStartedState中被处理。

最后转换到DisconnectedState状态,关注enter()函数:
public void enter() {            // We dont scan frequently if this is a temporary disconnect            // due to p2p            if (mTemporarilyDisconnectWifi) {                mWifiP2pChannel.sendMessage(WifiP2pServiceImpl.DISCONNECT_WIFI_RESPONSE);                return;            }            if (PDBG) {                logd(" Enter DisconnectedState scan interval "                        + mWifiConfigStore.wifiDisconnectedShortScanIntervalMilli.get()                        + " mLegacyPnoEnabled= " + mLegacyPnoEnabled                        + " screenOn=" + mScreenOn                        + " useGscan=" + mHalBasedPnoDriverSupported + "/"                        + mWifiConfigStore.enableHalBasedPno.get());            }            /** clear the roaming state, if we were roaming, we failed */            mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;            if (useHalBasedAutoJoinOffload()) {                startGScanDisconnectedModeOffload("disconnectedEnter");            } else {                if (mScreenOn) {                    /**                     * screen lit and => delayed timer                     */                    startDelayedScan(500, null, null);                } else {                    /**                     * screen dark and PNO supported => scan alarm disabled                     */                    if (mBackgroundScanSupported) {                        /* If a regular scan result is pending, do not initiate background                         * scan until the scan results are returned. This is needed because                        * initiating a background scan will cancel the regular scan and                        * scan results will not be returned until background scanning is                        * cleared                        */                        if (!mIsScanOngoing) {                            enableBackgroundScan(true);//启动wifi扫描,随后会触发autojoin,进行连接操作                        }                    } else {                        setScanAlarm(true);//启动一个扫描定时器                    }                }            }            /**             * If we have no networks saved, the supplicant stops doing the periodic scan.             * The scans are useful to notify the user of the presence of an open network.             * Note that these are not wake up scans.             */            if (mNoNetworksPeriodicScan != 0 && !mP2pConnected.get()                    && mWifiConfigStore.getConfiguredNetworks().size() == 0) {                sendMessageDelayed(obtainMessage(CMD_NO_NETWORKS_PERIODIC_SCAN,                        ++mPeriodicScanToken, 0), mNoNetworksPeriodicScan);            }            mDisconnectedTimeStamp = System.currentTimeMillis();            mDisconnectedPnoAlarmCount = 0;        }
在enter()函数中,会进行scan动作;WifiStateMachine处理SCAN_RESULTS_EVENT消息时,就会进入autojoin流程,尝试AP重连。
PS:流程图下载: http://download.csdn.net/detail/csdn_of_coder/9702484



更多相关文章

  1. Android 页面自动跳转方法(比如进入app的广告,通过Timer计时器,通过
  2. android 如何在状态栏上增加一个icon
  3. Rexsee API介绍:Android屏幕锁定,Keyguard函数与扩展源码
  4. Android基本控件和事件以及消息总结
  5. Android录制或播放语音消息时关闭其他媒体播放
  6. Android SharedPreferences保存登录状态
  7. Android 平台上,界面元素在定时器的响应函数里刷新。

随机推荐

  1. 进阶必读好书:《Python专家实践》
  2. 企业宣传视频、企业宣传视频制作
  3. JavaScript学习笔记(六)——递归函数
  4. 【阿里云镜像】配置阿里巴巴开源镜像站镜
  5. 【阿里云镜像】使用阿里云openssh镜像安
  6. 【阿里云镜像】OpenSUSE全新安装并更改阿
  7. JavaScript学习笔记(二十二)——原型及原型
  8. 用JS超简单的修改和删除cookie
  9. SpringMVC的默认参数类型的举例
  10. 2021Java面试题最新集锦