JasonWang's Blog

从Wifi热点功能说一说Android Wifi框架

字数统计: 4.5k阅读时长: 22 min
2021/04/29

之前帮着解决了项目中WIFI相关的问题, 一直想梳理下WIFI的框架, 方便后续代码阅读以及问题的解决. 恰好年初修改了车机上WIFI热点相关的一些代码, 重新看了下Android 10(Q)的逻辑, 于是想通过WIFI热点相关的功能作为切入点, 完整的梳理下Android WIFI的整体框架.

先不多说, 看下Android WIFI的大致框架:

android wifi framwork

从上至下, 对应的代码路径如下:

  • 应用层: /packages/apps/Settings
  • 框架层
    • /framework/opt/net/wifi/: wifiservice的核心逻辑
    • /framework/base/wifi: 公共接口部分
    • /framework/base/services/core/java/com/android/server/connectivity: 热点共享实现
  • native层
    • /hardware/interface/wifi: HAL层接口
    • /external/wpa_supplicant_8: 主要包括AP(access point)和STA(Station) 身份验证与数据加密的实现, 分为hostapdwpa_supplicant两个部分
    • /system/connectivity/wificond: wifi控制通道代码, 负责创建网口以及获取扫描结果等
    • /system/netd: 网络管理的守护进程, 负责路由/流量统计等相关的配置

接下来就来看下通过WIFI来分享热点时的具体代码流程.

开启热点

Android除了支持通过热点共享网络之外, 还支持通过USB/蓝牙的方式共享数据连接. 热点共享的接口都通过ConnectivityManager提供:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51


@UnsupportedAppUsage
public int tether(String iface) {
try {
String pkgName = mContext.getOpPackageName();
Log.i(TAG, "tether caller:" + pkgName);
return mService.tether(iface, pkgName);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}


@SystemApi
@RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
public void startTethering(int type, boolean showProvisioningUi,
final OnStartTetheringCallback callback) {
startTethering(type, showProvisioningUi, callback, null);
}



@SystemApi
@RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
public void startTethering(int type, boolean showProvisioningUi,
final OnStartTetheringCallback callback, Handler handler) {
Preconditions.checkNotNull(callback, "OnStartTetheringCallback cannot be null.");

ResultReceiver wrappedCallback = new ResultReceiver(handler) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (resultCode == TETHER_ERROR_NO_ERROR) {
callback.onTetheringStarted();
} else {
callback.onTetheringFailed();
}
}
};

try {
String pkgName = mContext.getOpPackageName();
Log.i(TAG, "startTethering caller:" + pkgName);
mService.startTethering(type, wrappedCallback, showProvisioningUi, pkgName);
} catch (RemoteException e) {
Log.e(TAG, "Exception trying to start tethering.", e);
wrappedCallback.send(TETHER_ERROR_SERVICE_UNAVAIL, null);
}
}


ConnectivityManager实际调用了ConnectivityService的接口, 这里看下startTethering这个接口的实现: 首先判断下是否支持热点共享, 然后调用mTethering.startTethering发起热点共享.

1
2
3
4
5
6
7
8
9
10
11
12
13
14


@Override
public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi,
String callerPkg) {
ConnectivityManager.enforceTetherChangePermission(mContext, callerPkg);
if (!isTetheringSupported()) {
receiver.send(ConnectivityManager.TETHER_ERROR_UNSUPPORTED, null);
return;
}
mTethering.startTethering(type, receiver, showProvisioningUi);
}


这个mTethering实际是在系统初始化ConnectivityService的时候创建的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22


mTethering = makeTethering();
....
protected Tethering makeTethering() {
// TODO: Move other elements into @Overridden getters.
final TetheringDependencies deps = new TetheringDependencies() {
@Override
public boolean isTetheringSupported() {
return ConnectivityService.this.isTetheringSupported();
}
@Override
public NetworkRequest getDefaultNetworkRequest() {
return mDefaultRequest;
}
};
return new Tethering(mContext, mNMS, mStatsService, mPolicyManager,
IoThread.get().getLooper(), new MockableSystemProperties(),
deps);
}


Tethering.java(代码位于/frameworks/base/services/core/java/com/android/server)的代码实现, 可以知道这部分主要起中介的作用:

  • 首先要根据热点共享的类型创建对应的网络, 比如通过WIFI共享的话需要先打开WIFI热点
  • 其次是要监听需要共享的网络连接(比如4G网络/以太网连接等), 将两个网络进行数据的转发配置

startTethering实际最后调用了一个内部实现enableTetheringInternal:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

private void enableTetheringInternal(int type, boolean enable, ResultReceiver receiver) {
int result;
switch (type) {
case TETHERING_WIFI:
result = setWifiTethering(enable);
sendTetherResult(receiver, result);
break;
case TETHERING_USB:
result = setUsbTethering(enable);
sendTetherResult(receiver, result);
break;
case TETHERING_BLUETOOTH:
setBluetoothTethering(enable, receiver);
break;
case TETHERING_P2P:
result = setP2pTethering(enable);
sendTetherResult(receiver, result);
break;
default:
Log.w(TAG, "Invalid tether type.");
sendTetherResult(receiver, TETHER_ERROR_UNKNOWN_IFACE);
}
}

这里会根据热点类型来创建对应的网络, 对于WIFI热点会调用setWifiTethering来开启WIFI热点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25


private int setWifiTethering(final boolean enable) {
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mPublicSync) {
final WifiManager mgr = getWifiManager();
if (mgr == null) {
mLog.e("setWifiTethering: failed to get WifiManager!");
return TETHER_ERROR_SERVICE_UNAVAIL;
}
if ((enable && mgr.startSoftAp(null /* use existing wifi config */)) ||
(!enable && mgr.stopSoftAp())) {
mWifiTetherRequested = enable;
return TETHER_ERROR_NO_ERROR;
}
}
} finally {
Binder.restoreCallingIdentity(ident);
}

return TETHER_ERROR_MASTER_ERROR;
}


到这里, 就完成了第一步, 把WIFI热点开起来了. 再来看下WIFI热点的创建流程. 这个才是整个流程的关键步骤.

创建WIFI热点

为了更清晰的看到WIFI AP的流程, 这里分为两个部分来阐述具体的代码:

  • Java Framework的逻辑
  • Native的代码逻辑

创建WIFI热点-Framework流程

WifiManager(代码位于/framework/base/wifi)的startSoftAp实际就是调用了WifiService的接口而已, 我们直接看下WifiService的实现逻辑WifiServiceImpl(/framework/opt/net/wifi):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30


@Override
public boolean startSoftAp(WifiConfiguration wifiConfig) {
// NETWORK_STACK is a signature only permission.
enforceNetworkStackPermission();
// If we're in crypt debounce, ignore any wifi state change APIs.
if (mFrameworkFacade.inStorageManagerCryptKeeperBounce()) {
return false;
}

mLog.info("startSoftAp uid=%").c(Binder.getCallingUid()).flush();

synchronized (mLocalOnlyHotspotRequests) {
// If a tethering request comes in while we have an existing tethering session, return
// error.
if (mIfaceIpModes.contains(WifiManager.IFACE_IP_MODE_TETHERED)) {
mLog.err("Tethering is already active.").flush();
return false;
}
// If a tethering request comes in while we have LOHS running (or requested), call stop
// for softap mode and restart softap with the tethering config.
if (!isConcurrentLohsAndTetheringSupported() && !mLocalOnlyHotspotRequests.isEmpty()) {
stopSoftApInternal(WifiManager.IFACE_IP_MODE_LOCAL_ONLY);
}
return startSoftApInternal(wifiConfig, WifiManager.IFACE_IP_MODE_TETHERED);
}
}


这个函数首先会判断是否有本地热点(Local Only Hotspot, 就是无法使用网络连接的热点, 只用于两个设备之间通讯), 如果有的话, 就开启一个非共享的热点, 否则开启一个有共享的热点. 这里只看下有共享的流程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17


private boolean startSoftApInternal(WifiConfiguration wifiConfig, int mode) {
mLog.trace("startSoftApInternal uid=% mode=%")
.c(Binder.getCallingUid()).c(mode).flush();

// null wifiConfig is a meaningful input for CMD_SET_AP
if (wifiConfig == null || WifiApConfigStore.validateApWifiConfiguration(wifiConfig)) {
SoftApModeConfiguration softApConfig = new SoftApModeConfiguration(mode, wifiConfig);
mWifiController.sendMessage(CMD_SET_AP, 1, 0, softApConfig);
return true;
}
Slog.e(TAG, "Invalid WifiConfiguration");
return false;
}


由于Wifi Framework的代码涉及到STA/AP两个模式, 有不少的状态机代码;在查看具体代码流程之前, 先抛出一个具体的代码流程方便跟踪:

1
2
3
4
5


WifiServiceImpl --> WifiController --> ActiveModeWarden --> SoftApManager --> WifiNative --> HostapdHal


WifiController是一个用于管控Wifi内部状态的状态机, 其有如下几个状态:

  • 默认状态DefaultState: 其他状态的父状态
  • STA开启状态StaEnabledState: Wifi打开, 处于STA模式, 这个时候会进行热点扫描
  • STA关闭状态StaDisabledState: Wifi关闭, STA模式关闭, 无法进行扫描
  • STA关闭可扫描状态StaDisabledWithScanState: STA模式关闭, 但WIFI会进入扫描模式
  • ECM状态EcmState: 紧急呼叫状态(如手机终端正在进行紧急呼叫等)
1
2
3
4
5
6
7
8
9


private DefaultState mDefaultState = new DefaultState();
private StaEnabledState mStaEnabledState = new StaEnabledState();
private StaDisabledState mStaDisabledState = new StaDisabledState();
private StaDisabledWithScanState mStaDisabledWithScanState = new StaDisabledWithScanState();
private EcmState mEcmState = new EcmState();


一般来说, WifiService启动时的初始状态为StaDisabledState, 如果默认WIFI是开的, 则会进入StaEnabledState状态, 这里假定系统初始状态为StaDisabledState. 从上面的流程可以看到, WifiService实际向WifiController发送了一个消息:

1
2
3

mWifiController.sendMessage(CMD_SET_AP, 1, 0, softApConfig);

此时, StaDisabledState状态机会把消息丢给DefaultState处理(返回了NOT_HANDLED):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

class StaDisabledState extends State {
private int mDeferredEnableSerialNumber = 0;
private boolean mHaveDeferredEnable = false;
private long mDisabledTimestamp;

@Override
public void enter() {
mActiveModeWarden.disableWifi();
// Supplicant can't restart right away, so note the time we switched off
mDisabledTimestamp = SystemClock.elapsedRealtime();
mDeferredEnableSerialNumber++;
mHaveDeferredEnable = false;
}

@Override
public boolean processMessage(Message msg) {
switch (msg.what) {
case CMD_WIFI_TOGGLED:
if (mSettingsStore.isWifiToggleEnabled()) {
if (doDeferEnable(msg)) {
if (mHaveDeferredEnable) {
// have 2 toggles now, inc serial number and ignore both
mDeferredEnableSerialNumber++;
}
mHaveDeferredEnable = !mHaveDeferredEnable;
break;
}
transitionTo(mStaEnabledState);
} else if (checkScanOnlyModeAvailable()) {
// only go to scan mode if we aren't in airplane mode
if (mSettingsStore.isAirplaneModeOn()) {
transitionTo(mStaDisabledWithScanState);
}
}
break;
....
case CMD_SET_AP:
if (msg.arg1 == 1) {
// remember that we were disabled, but pass the command up to start softap
mSettingsStore.setWifiSavedState(WifiSettingsStore.WIFI_DISABLED);
}
return NOT_HANDLED;
....
default:
return NOT_HANDLED;
}
return HANDLED;
}
....
}


DefaultState状态收到CMD_SET_AP的消息后会尝试调用ActiveModeWarden.enterSoftAPMode开启AP:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54

class DefaultState extends State {
@Override
public boolean processMessage(Message msg) {
switch (msg.what) {
....
case CMD_SET_AP:
// note: CMD_SET_AP is handled/dropped in ECM mode - will not start here
if (msg.arg1 == 1) {
SoftApModeConfiguration config = (SoftApModeConfiguration) msg.obj;
mActiveModeWarden.enterSoftAPMode((SoftApModeConfiguration) msg.obj);
} else {
mActiveModeWarden.stopSoftAPMode(msg.arg2);
}
break;
case CMD_AIRPLANE_TOGGLED:
if (mSettingsStore.isAirplaneModeOn()) {
log("Airplane mode toggled, shutdown all modes");
mActiveModeWarden.shutdownWifi();
transitionTo(mStaDisabledState);
} else {
log("Airplane mode disabled, determine next state");
if (mSettingsStore.isWifiToggleEnabled()) {
transitionTo(mStaEnabledState);
} else if (checkScanOnlyModeAvailable()) {
transitionTo(mStaDisabledWithScanState);
}
// wifi should remain disabled, do not need to transition
}
break;
case CMD_EMERGENCY_CALL_STATE_CHANGED:
case CMD_EMERGENCY_MODE_CHANGED:
if (msg.arg1 == 1) {
transitionTo(mEcmState);
}
break;
case CMD_AP_STOPPED:
log("SoftAp mode disabled, determine next state");
if (mSettingsStore.isWifiToggleEnabled()) {
transitionTo(mStaEnabledState);
} else if (checkScanOnlyModeAvailable()) {
transitionTo(mStaDisabledWithScanState);
}
// wifi should remain disabled, do not need to transition
break;
default:
throw new RuntimeException("WifiController.handleMessage " + msg.what);
}
return HANDLED;
}

}


ActiveModeWarden这个类的主要作用是负责WIFI不同操作模式的切换, 比如从STA模式切换到扫描模式, 从AP切换到STA等等:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

public void enterSoftAPMode(@NonNull SoftApModeConfiguration wifiConfig) {
mHandler.post(() -> {
startSoftAp(wifiConfig);
});
}


private void startSoftAp(SoftApModeConfiguration softapConfig) {
Log.d(TAG, "Starting SoftApModeManager");

WifiConfiguration config = softapConfig.getWifiConfiguration();
if (config != null && config.SSID != null) {
Log.d(TAG, "Passing config to SoftApManager! " + config);
} else {
config = null;
}

SoftApCallbackImpl callback = new SoftApCallbackImpl(softapConfig.getTargetMode());
ActiveModeManager manager = mWifiInjector.makeSoftApManager(callback, softapConfig);
callback.setActiveModeManager(manager);
manager.start();
mActiveModeManagers.add(manager);
updateBatteryStatsWifiState(true);
}


makeSoftApManager实际构造了一个SoftApManager对象实例, 然后调用start启动AP的创建. 这里mStateMachineSoftApManager内部的一个状态机SoftApStateMachine, 其有两个状态: IdleState/StartedState, 其初始状态为Idle:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22


/**
* Start soft AP with the supplied config.
*/
public void start() {
mStateMachine.sendMessage(SoftApStateMachine.CMD_START, mApConfig);
}


// 内部状态机
SoftApStateMachine(Looper looper) {
super(TAG, looper);

addState(mIdleState);
addState(mStartedState);

setInitialState(mIdleState);
start();
}


IdleState在收到CMD_START的消息后, 主要做了如下几件事情:

  • 首先通过WifiNative创建一个AP的网口
  • 调用startSoftAp执行AP的开启: 比如设置WIFI的国家码(不同地区有不同的5G频段)
  • 进入StartedState状态, 等待AP开启
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

private class IdleState extends State {
@Override
public void enter() {
mApInterfaceName = null;
mIfaceIsUp = false;
mIfaceIsDestroyed = false;
}

@Override
public boolean processMessage(Message message) {
switch (message.what) {
case CMD_START:
....
mApInterfaceName = mWifiNative.setupInterfaceForSoftApMode(
mWifiNativeInterfaceCallback);
if (TextUtils.isEmpty(mApInterfaceName)) {
Log.e(TAG, "setup failure when creating ap interface.");
updateApState(WifiManager.WIFI_AP_STATE_FAILED,
WifiManager.WIFI_AP_STATE_DISABLED,
WifiManager.SAP_START_FAILURE_GENERAL);
mWifiMetrics.incrementSoftApStartResult(
false, WifiManager.SAP_START_FAILURE_GENERAL);
break;
}
updateApState(WifiManager.WIFI_AP_STATE_ENABLING,
WifiManager.WIFI_AP_STATE_DISABLED, 0);
int result = startSoftAp((WifiConfiguration) message.obj);
if (result != SUCCESS) {
int failureReason = WifiManager.SAP_START_FAILURE_GENERAL;
if (result == ERROR_NO_CHANNEL) {
failureReason = WifiManager.SAP_START_FAILURE_NO_CHANNEL;
}
updateApState(WifiManager.WIFI_AP_STATE_FAILED,
WifiManager.WIFI_AP_STATE_ENABLING,
failureReason);
stopSoftAp();
mWifiMetrics.incrementSoftApStartResult(false, failureReason);
break;
}
transitionTo(mStartedState);
break;
default:
// Ignore all other commands.
break;
}

return HANDLED;
}
}


至此, 最后AP的创建都是通过WifiNative调用Native的接口来实现热点的创建:

  • 首先是通过WifiNative.setupInterfaceForSoftApMode创建AP网口, 并启动hostapd
  • 然后通过WifiNative.startSoftAp启动AP, 准备接受来自客户端的连接请求

接下来就看看这两个流程对应的Native代码.

创建WIFI热点-Native流程

WIFI热点的创建在Native层主要依赖两个服务来完成:

  • WifiCond: 负责创建AP的网口wlan0, 并获取当前的热点数量等信息
  • Hostapd: 负责热点连接的管理以及身份验证等

接着上面的流程, WifiNative.setupInterfaceForSoftApMode这个方法的首先会尝试启动Hostapd守护进程, 接着调用WifiCondControl.setupInterfaceForSoftApMode来创建一个AP接口.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51


public String setupInterfaceForSoftApMode(@NonNull InterfaceCallback interfaceCallback) {
synchronized (mLock) {
if (!startHal()) {
Log.e(TAG, "Failed to start Hal");
mWifiMetrics.incrementNumSetupSoftApInterfaceFailureDueToHal();
return null;
}
if (!startHostapd()) {
Log.e(TAG, "Failed to start hostapd");
mWifiMetrics.incrementNumSetupSoftApInterfaceFailureDueToHostapd();
return null;
}
Iface iface = mIfaceMgr.allocateIface(Iface.IFACE_TYPE_AP);
if (iface == null) {
Log.e(TAG, "Failed to allocate new AP iface");
return null;
}
iface.externalListener = interfaceCallback;
iface.name = createApIface(iface);
if (TextUtils.isEmpty(iface.name)) {
Log.e(TAG, "Failed to create AP iface in vendor HAL");
mIfaceMgr.removeIface(iface.id);
mWifiMetrics.incrementNumSetupSoftApInterfaceFailureDueToHal();
return null;
}
// 调用HAL接口创建AP网口
if (mWificondControl.setupInterfaceForSoftApMode(iface.name) == null) {
Log.e(TAG, "Failed to setup iface in wificond on " + iface);
teardownInterface(iface.name);
mWifiMetrics.incrementNumSetupSoftApInterfaceFailureDueToWificond();
return null;
}
iface.networkObserver = new NetworkObserverInternal(iface.id);
if (!registerNetworkObserver(iface.networkObserver)) {
Log.e(TAG, "Failed to register network observer on " + iface);
teardownInterface(iface.name);
return null;
}
// Just to avoid any race conditions with interface state change callbacks,
// update the interface state before we exit.
onInterfaceStateChanged(iface, isInterfaceUp(iface.name));
Log.i(TAG, "Successfully setup " + iface);

iface.featureSet = getSupportedFeatureSetInternal(iface.name);
return iface.name;
}
}


到这里, 实际就是调用IWifiCond这个HAL接口来创建AP的网口. WifiCond这个服务在系统启动的时候加载的. 我们不妨直接跳到/system/connectivity/wificond来看下WifiCond服务的实现逻辑.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

// WifiCondControl
public IApInterface setupInterfaceForSoftApMode(@NonNull String ifaceName) {
Log.d(TAG, "Setting up interface for soft ap mode");
if (!retrieveWificondAndRegisterForDeath()) {
return null;
}

IApInterface apInterface = null;
try {
apInterface = mWificond.createApInterface(ifaceName);
} catch (RemoteException e1) {
Log.e(TAG, "Failed to get IApInterface due to remote exception");
return null;
}
....
Binder.allowBlocking(apInterface.asBinder());

// Refresh Handlers
mApInterfaces.put(ifaceName, apInterface);
return apInterface;
}

IWifiCond的实现都在Server.cpp中, createApInterface这个函数实际创建一个接口, 并返回IApInterface的远程调用接口给到上层使用.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

//Server.cpp
Status Server::createApInterface(const std::string& iface_name,
sp<IApInterface>* created_interface) {
InterfaceInfo interface;
if (!SetupInterface(iface_name, &interface)) {
return Status::ok(); // Logging was done internally
}

unique_ptr<ApInterfaceImpl> ap_interface(new ApInterfaceImpl(
interface.name,
interface.index,
interface.is_bridge,
netlink_utils_,
if_tool_.get()));
*created_interface = ap_interface->GetBinder();
BroadcastApInterfaceReady(ap_interface->GetBinder());
ap_interfaces_[iface_name] = std::move(ap_interface);

return Status::ok();
}


到这里, 热点的创建算是完成了第一步. 等到WifiCond创建完成之后, wlanx的网口UP之后, 会将状态回调给SoftApManager, 在这里会发送一个热点已经开启成功的广播, Tethering服务收到该广播之后, 就会为该网口分配IP, 并启动DHCP相关的进程, 这样就可以愉快的接受来自客户端的连接请求了.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

// SoftApManager
private void onUpChanged(boolean isUp) {
if (isUp == mIfaceIsUp) {
return; // no change
}
mIfaceIsUp = isUp;
if (isUp) {
Log.d(TAG, "SoftAp is ready for use");
updateApState(WifiManager.WIFI_AP_STATE_ENABLED,
WifiManager.WIFI_AP_STATE_ENABLING, 0);
mWifiMetrics.incrementSoftApStartResult(true, 0);
if (mCallback != null) {
mCallback.onNumClientsChanged(mNumAssociatedStations);
}
} else {
// the interface was up, but goes down
sendMessage(CMD_INTERFACE_DOWN);
}
mWifiMetrics.addSoftApUpChangedEvent(isUp, mMode);
}

private void updateApState(int newState, int currentState, int reason) {
mCallback.onStateChanged(newState, reason);

//send the AP state change broadcast
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, newState);
intent.putExtra(WifiManager.EXTRA_PREVIOUS_WIFI_AP_STATE, currentState);
if (newState == WifiManager.WIFI_AP_STATE_FAILED) {
//only set reason number when softAP start failed
intent.putExtra(WifiManager.EXTRA_WIFI_AP_FAILURE_REASON, reason);
}

intent.putExtra(WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME, mApInterfaceName);
intent.putExtra(WifiManager.EXTRA_WIFI_AP_MODE, mMode);
mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}

网络配置与DHCP服务的启动

上面说到, WIFI热点的网口UP之后, 会发送一个WIFI_AP_STATE_CHANGED_ACTION的广播, 而Tethering服务在启动的时候就监听了该广播:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

private class StateReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context content, Intent intent) {
final String action = intent.getAction();
if (action == null) return;

if (action.equals(UsbManager.ACTION_USB_STATE)) {
handleUsbAction(intent);
} else if (action.equals(CONNECTIVITY_ACTION)) {
handleConnectivityAction(intent);
} else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
handleWifiApAction(intent);
} else if (action.equals(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION)) {
handleWifiP2pAction(intent);
} else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
mLog.log("OBSERVED configuration changed");
updateConfiguration();
}
}

这里对应就会调用handleWifiApAction: 根据AP的状态来执行对应的动作.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

private void handleWifiApAction(Intent intent) {
final int curState = intent.getIntExtra(EXTRA_WIFI_AP_STATE, WIFI_AP_STATE_DISABLED);
final String ifname = intent.getStringExtra(EXTRA_WIFI_AP_INTERFACE_NAME);
final int ipmode = intent.getIntExtra(EXTRA_WIFI_AP_MODE, IFACE_IP_MODE_UNSPECIFIED);

synchronized (Tethering.this.mPublicSync) {
switch (curState) {
case WifiManager.WIFI_AP_STATE_ENABLING:
// We can see this state on the way to both enabled and failure states.
break;
case WifiManager.WIFI_AP_STATE_ENABLED:
enableWifiIpServingLocked(ifname, ipmode);
break;
case WifiManager.WIFI_AP_STATE_DISABLED:
case WifiManager.WIFI_AP_STATE_DISABLING:
case WifiManager.WIFI_AP_STATE_FAILED:
default:
disableWifiIpServingLocked(ifname, curState);
break;
}
}
}



根据热点开启模式(共享还是本地)开启IP配置流程:

  • maybeTrackNewInterfaceLocked: 开启一个IpServer的状态机用于配置热点IP以及启动DHCP服务
  • changeInterfaceState: 让IpServer进入热点共享状态, 等待客户端请求以及上游网络连接(比如4G)

到这里, 我们大致可以看到, 热点共享功能还需要如下3个步骤才能最终完成:

  • 配置wlanx网口IP地址
  • 启动DHCP服务
  • 如果发现当前系统有可用的网络连接, 则将热点的数据口wlanx转发到对应的网口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

private void enableWifiIpServingLocked(String ifname, int wifiIpMode) {
// Map wifiIpMode values to IpServer.Callback serving states, inferring
// from mWifiTetherRequested as a final "best guess".
final int ipServingMode;
switch (wifiIpMode) {
case IFACE_IP_MODE_TETHERED:
ipServingMode = IpServer.STATE_TETHERED;
break;
case IFACE_IP_MODE_LOCAL_ONLY:
ipServingMode = IpServer.STATE_LOCAL_ONLY;
break;
default:
mLog.e("Cannot enable IP serving in unknown WiFi mode: " + wifiIpMode);
return;
}

if (!TextUtils.isEmpty(ifname)) {
maybeTrackNewInterfaceLocked(ifname, TETHERING_WIFI);
changeInterfaceState(ifname, ipServingMode);
} else {
mLog.e(String.format(
"Cannot enable IP serving in mode %s on missing interface name",
ipServingMode));
}
}


实际上IpServer就是一个状态机, 其有InitialState/LocalHotspotState/TetheredState/UnavailableState这么四个状态, 其初始状态为InitialState; 在状态机收到消息CMD_TETHER_REQUESTED后, 就会进入TetheredState状态.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19


private void maybeTrackNewInterfaceLocked(final String iface, int interfaceType) {
// If we have already started a TISM for this interface, skip.
if (mTetherStates.containsKey(iface)) {
mLog.log("active iface (" + iface + ") reported as added, ignoring");
return;
}

mLog.log("adding TetheringInterfaceStateMachine for: " + iface);
final TetherState tetherState = new TetherState(
new IpServer(iface, mLooper, interfaceType, mLog, mNMService, mStatsService,
makeControlCallback(), mConfig.enableLegacyDhcpServer,
mDeps.getIpServerDependencies()));
mTetherStates.put(iface, tetherState);
tetherState.ipServer.start();
}


IpServer在进入TetheredState之后主要是配置IP/路由以及启动DHCP;需要说明的是, Android Q实际使用了两套DHCP逻辑,一个是通过dnsmasq来实现(LegacyDhcpServer); 一个是通过一个Java进程来实现. 目前默认使用的是通过Java进程(/packages/modules/networkstack)来实现的. 有兴趣的可以自行看下具体的代码实现.

IpServer中的TetheredState继承了BaseServingState, 因此实际进入TetheredState时就会做如下几个事情:

  • startIPv4: 配置网口IP地址, 启动DHCP服务
  • mNMService.tetherInterface: 添加路由信息
  • startIPv6: 启动IPv6路由广播(Route Advertisement)守护进程
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28


class BaseServingState extends State {
@Override
public void enter() {
if (!startIPv4()) {
mLastError = ConnectivityManager.TETHER_ERROR_IFACE_CFG_ERROR;
return;
}

try {
mNMService.tetherInterface(mIfaceName);
} catch (Exception e) {
mLog.e("Error Tethering: " + e);
mLastError = ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR;
return;
}

if (!startIPv6()) {
mLog.e("Failed to startIPv6");
// TODO: Make this a fatal error once Bluetooth IPv6 is sorted.
return;
}
}
...
}


到这里, 热点共享功能也算启动完成了, 对于非本地功能的热点, 还需要完成最后一件事情: 监听系统当前默认网络连接(具备上网功能), 然后热点网口数据转发到默认数据网口, 这样连接过来的热点就可以访问外网了. 这里只是来看下IpServer在收到默认网络可用时的处理流程, 具体是如何监听系统当前的网络状态, 可以参考Tethering.java/UpStreamNetworkMonitor.java这两个文件中代码.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68

class TetheredState extends BaseServingState {
@Override
public void enter() {
super.enter();
if (mLastError != ConnectivityManager.TETHER_ERROR_NO_ERROR) {
transitionTo(mInitialState);
}

if (DBG) Log.d(TAG, "Tethered " + mIfaceName);
sendInterfaceState(STATE_TETHERED);
}

...

@Override
public boolean processMessage(Message message) {
if (super.processMessage(message)) return true;

logMessage(this, message.what);
switch (message.what) {
case CMD_TETHER_REQUESTED:
mLog.e("CMD_TETHER_REQUESTED while already tethering.");
break;
// 上游网络连接可用
case CMD_TETHER_CONNECTION_CHANGED:
final InterfaceSet newUpstreamIfaceSet = (InterfaceSet) message.obj;
if (noChangeInUpstreamIfaceSet(newUpstreamIfaceSet)) {
if (VDBG) Log.d(TAG, "Connection changed noop - dropping");
break;
}

if (newUpstreamIfaceSet == null) {
cleanupUpstream();
break;
}

for (String removed : upstreamInterfacesRemoved(newUpstreamIfaceSet)) {
cleanupUpstreamInterface(removed);
}

final Set<String> added = upstreamInterfacesAdd(newUpstreamIfaceSet);
// This makes the call to cleanupUpstream() in the error
// path for any interface neatly cleanup all the interfaces.
mUpstreamIfaceSet = newUpstreamIfaceSet;

for (String ifname : added) {
try {
mNMService.enableNat(mIfaceName, ifname);
mNMService.startInterfaceForwarding(mIfaceName, ifname);
} catch (Exception e) {
mLog.e("Exception enabling NAT: " + e);
cleanupUpstream();
mLastError = ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR;
transitionTo(mInitialState);
return true;
}
}
break;
default:
return false;
}
return true;
}
....
}


总结

Android WIFI模块设计的代码量比较大, 涉及到的模块也比较多, 要吃透其中的流程, 首先要建立一个整体的代码框架, 其次要通过某个具体的应用场景一步步梳理, 才能做到阅读代码时心中有数.

原文作者:Jason Wang

更新日期:2022-02-16, 15:05:54

版权声明:本文采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可

CATALOG
  1. 1. 开启热点
  2. 2. 创建WIFI热点
    1. 2.1. 创建WIFI热点-Framework流程
    2. 2.2. 创建WIFI热点-Native流程
  3. 3. 网络配置与DHCP服务的启动
  4. 4. 总结