Recently, I studied how Android Wi-Fi subsystem works. I was more specifically
interested to learn the scan behavior. The source code related to this is mainly
in framework/base/wifi/java/android/net/wifi/
within AOSP source tree.
The Big Picture
Android uses a customized wpa_supplicant to perform AP authentication and
association, and also communicate with underlying driver. The WifiNative
class
is used to send various commands to wpa_supplicant ,and the WifiMonitor
class
is used to monitor wpa_supplicant status change and notify Android framework.
wpa_supplicant communicates with underlying driver using new CFG80211/NL80211 interface.
Basics of Hierarchical State Machine
Android framework uses a Hierarchical State Machine (HSM) to maintain the different states of Wi-Fi connection. As the name indicates, all states are organized in a tree, and there is one special initial state. The interface of each state is as follows:
enter()
: called when entering this state.exit()
: called when exiting this state.processMessage()
: called when message arrives.
The most import property of HSM is that when transitioning between states, we first found the common ancestor state that's closest to current state, the we exit from current state and all its ancestor state up to but not include the closest common ancestor, then enter all of the new states below the closet common ancestor down to the new state.
Here is a simple example HSM.
S4
is the initial state. When we first start the HSM, S0.enter()
,
S1.enter()
and S4.enter()
will be called in sequence. Suppose we want to
transit from S0
to S7
, since the closet common ancestor is S0
, S4.exit()
S1.exit()
, S2.enter()
, S5.enter()
and S7.enter()
will be called in
sequence.
More details about HSM can be found in the comments of
frameworks/base/core/java/com/android/internal/util/StateMachin.java
.
Wifi State Machine
Here is a subset of the whole Android Wifi HSM, states about P2P connections are omitted.
So in Initial state's enter()
function, we check if the driver is loaded, and
transit to Driver Loaded state if yes. Then we start wpa_supplicant and transit
When we receive SUP_CONNECTED_EVENT
, we switch to Driver Started state. But
before that, we need to first enter Supplicant Started state first. In the
enter()
function of Supplicant Started state, we set the supplicant scan
interval, which, by default, is 15 seconds defined in
frameworks/base/core/res/res/values/config.xml
as
config_wifi_supplicant_scan_interval
. So the first fact of Android scan
behavior is that it'll do scan every 15 seconds as long as the wpa_supplicant
is started, no matter what the Wi-Fi condition is.
Then we come to Driver Started State, if we're not in scan mode, then we switch to Disconnected Mode state. Scan mode means that Wi-Fi will be kept active, but the only operation that will be supported is initiation of scans, and the subsequent reporting of scan results. No attempts will be made to automatically connect to remembered access points, nor will periodic scans be automatically performed looking for remembered access points.
In Disconnected Mode state's enter()
function, if the chipset does not support
background scan, then we enable framework periodic scan. The default interval is
300 seconds (5 mins), defined in framworks/base/core/res/res/values/config.xml
as config_wifi_framework_scan_interval
. So the second behavior of Android scan
is that, in disconnected mode, it'll issue scan every 5 mins.
Then if received NETWORK_CONNECTION_EVENT
event from WifiMonitor
, we switch
to Obtaining IP state, which will initiate the DHCP process if needed. Then we
go through Veifying Link and Captive Portal Check state, and finally reach
Connected state.
WifiWatchdogStateMachine
will continuously monitor the link quality and packet
loss event, and will send out POOR_LINK_DETECTED
or GOOD_LINK_DETECTED
event.
Android Scan Interval
Here is the statistics of scan interval distribution collected on 129 Nexus S phones for about 5 months.
We can see that there are 4 peaks in the distribution. The peak around 15 seconds is due to wpa_supplicant scan interval, and the peak around 300 is due to framework periodic scan. The peak around 60 seconds is not much clear yet, probably due to the scan interval when P2P is connected.
The interesting fact is actually the peak within 2 seconds. It seems most of the scan results are clustered together in a small time windows (1~2 seconds). This is because when the driver is scanning, it'll report every time it detects one AP. So in one scan, multiple scan result event will be triggered. And every time when there is a low level scan result event, Android will report the complete updated scan result list.