从刚接触WiFi时跟过wifi的开启流程,当时还是android9。到了Android11代码架构有了不小的改动,在这里重新梳理一遍,便于在工作中更快速的跟踪代码。
一、Settings里改动不大,还是从WifiEnabler开始,调用WiFiManager的setWifiEnabled。
packages/apps/Settings/src/com/android/settings/wifi/WifiEnabler.java
if (!mWifiManager.setWifiEnabled(isChecked)) {
// Error
mSwitchWidget.setEnabled(true);
Toast.makeText(mContext, R.string.wifi_error, Toast.LENGTH_SHORT).show();
}
二、这里要注意了,Android11默认加入了支持双WiFi的代码。这里打开WiFi就提供了俩个接口
frameworks/base/wifi/java/android/net/wifi/WifiManager.java
正常打开WiFi是调用这个单参的函数。
public boolean setWifiEnabled(boolean enabled) {
try {
return mService.setWifiEnabled(mContext.getOpPackageName(), enabled);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
如果是指定打开哪个STA,就要调用双参的函数。
public boolean setWifiEnabled(int staId, boolean enabled) {
try {
return mService.setWifiEnabled2(mContext.getOpPackageName(), staId, enabled);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
三、可以看到Wifimanager中正常打开WiFi和指定打开哪个STA的区别就是在WifiServiceImpl中setWifiEnabled2的参数不同。如果是打开第一个WiFi,则参数2为STA_PRIMARY,如果是打开其他WiFi,则参数2为传入的staId。
frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiServiceImpl.java
public synchronized boolean setWifiEnabled(String packageName, boolean enable) {
return setWifiEnabled2(packageName, STA_PRIMARY, enable);
}
public synchronized boolean setWifiEnabled2(String packageName, int staId,boolean enable) {
if (enforceChangePermission(packageName) != MODE_ALLOWED) {
return false;
}
boolean isPrivileged = isPrivileged(Binder.getCallingPid(), Binder.getCallingUid());
if (!isPrivileged && !isDeviceOrProfileOwner(Binder.getCallingUid(), packageName)
&& !mWifiPermissionsUtil.isTargetSdkLessThan(packageName, Build.VERSION_CODES.Q,
Binder.getCallingUid())
&& !isSystem(packageName, Binder.getCallingUid())) {
mLog.info("setWifiEnabled not allowed for uid=%")
.c(Binder.getCallingUid()).flush();
return false;
}
// If Airplane mode is enabled, only privileged apps are allowed to toggle Wifi
if (mSettingsStore.isAirplaneModeOn() && !isPrivileged) {
mLog.err("setWifiEnabled in Airplane mode: only Settings can toggle wifi").flush();
return false;
}
// If SoftAp is enabled, only privileged apps are allowed to toggle wifi
if (!isPrivileged && mTetheredSoftApTracker.getState() == WIFI_AP_STATE_ENABLED) {
mLog.err("setWifiEnabled with SoftAp enabled: only Settings can toggle wifi").flush();
return false;
}
mLog.info("setWifiEnabled package=% uid=% enable=%").c(packageName)
.c(Binder.getCallingUid()).c(enable).flush();
long ident = Binder.clearCallingIdentity();
try {
if (staId == STA_PRIMARY && !mSettingsStore.handleWifiToggled(enable)) {
// Nothing to do if wifi cannot be toggled
return true;
}
} finally {
Binder.restoreCallingIdentity(ident);
}
if (mWifiPermissionsUtil.checkNetworkSettingsPermission(Binder.getCallingUid())) {
mWifiMetrics.logUserActionEvent(enable ? UserActionEvent.EVENT_TOGGLE_WIFI_ON
: UserActionEvent.EVENT_TOGGLE_WIFI_OFF);
}
if (!mIsControllerStarted) {
Log.e(TAG,"WifiController is not yet started, abort setWifiEnabled");
return false;
}
mWifiMetrics.incrementNumWifiToggles(isPrivileged, enable);
if(staId == STA_PRIMARY)
mActiveModeWarden.wifiToggled();
else if(staId == STA_SECONDARY && (getNumConcurrentStaSupported() > 1) && (getWifiEnabledState() == WifiManager.WIFI_STATE_ENABLED))
mActiveModeWarden.qtiWifiToggled(staId, enable);
else
Log.e(TAG,"setWifiEnabled not allowed for Id: " + staId);
return true;
}
四、可以看到wifiservice调用了ActiveModeWarden的wifiToggled,发送了CMD_WIFI_TOGGLED的消息,通知WiFi切换了。
frameworks/opt/net/wifi/service/java/com/android/server/wifi/ActiveModeWarden.java
public void wifiToggled() {
mWifiController.sendMessage(WifiController.CMD_WIFI_TOGGLED);
}
五、我们看WifiController是怎么处理这个消息的。WifiController是ActiveModeWarden中的一个状态机,用来管理WiFi的操作,包括热点啊飞行模式什么的。
打开WiFi之前,状态机应该是在Disabled状态,我们看Disable状态里的处理。
class DisabledState extends BaseState {
public boolean processMessageFiltered(Message msg) {
switch (msg.what) {
case CMD_WIFI_TOGGLED:
case CMD_SCAN_ALWAYS_MODE_CHANGED:
if (shouldEnableSta()) {
startClientModeManager();
transitionTo(mEnabledState);
}
break;
启动一个新的客户端管理。
private boolean startClientModeManager() {
Log.d(TAG, "Starting ClientModeManager");
ClientListener listener = new ClientListener();
ClientModeManager manager = mWifiInjector.makeClientModeManager(listener);
listener.setActiveModeManager(manager);
manager.start();
if (!switchClientModeManagerRole(manager)) {
return false;
}
mActiveModeManagers.add(manager);
return true;
}
六、start了ClientModeManager
frameworks/opt/net/wifi/service/java/com/android/server/wifi/ClientModeManager.java
public void start() {
Log.d(TAG, "Starting with role ROLE_CLIENT_SCAN_ONLY");
mRole = ROLE_CLIENT_SCAN_ONLY;
mTargetRole = ROLE_CLIENT_SCAN_ONLY;
mStateMachine.sendMessage(ClientModeStateMachine.CMD_START);
}
看一下是谁处理了这个START消息呢
private class IdleState extends State {
@Override
public boolean processMessage(Message message) {
switch (message.what) {
case CMD_START:
// Always start in scan mode first.
mClientInterfaceName =
mWifiNative.setupInterfaceForClientInScanMode(
mWifiNativeInterfaceCallback);
if (TextUtils.isEmpty(mClientInterfaceName)) {
Log.e(TAG, "Failed to create ClientInterface. Sit in Idle");
mModeListener.onStartFailure();
break;
}
transitionTo(mScanOnlyModeState);
break;
}
}
七、这里可以看出,WifiNative先去启动HAL
frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiNative.java
public String setupInterfaceForClientInScanMode(
@NonNull InterfaceCallback interfaceCallback) {
synchronized (mLock) {
if (!startHal()) {
mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToHal();
return null;
}
Iface iface = mIfaceMgr.allocateIface(Iface.IFACE_TYPE_STA_FOR_SCAN);
iface.externalListener = interfaceCallback;
iface.name = createStaIface(iface);
if (!mWifiCondManager.setupInterfaceForClientMode(iface.name, Runnable::run,
new NormalScanEventCallback(iface.name),
new PnoScanEventCallback(iface.name))) {
Log.e(TAG, "Failed to setup iface in wificond=" + iface.name);
teardownInterface(iface.name);
mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToWificond();
return null;
}
iface.networkObserver = new NetworkObserverInternal(iface.id);
if (!registerNetworkObserver(iface.networkObserver)) {
teardownInterface(iface.name);
return null;
}
mWifiMonitor.startMonitoring(iface.name);
onInterfaceStateChanged(iface, isInterfaceUp(iface.name));
iface.featureSet = getSupportedFeatureSetInternal(iface.name);
return iface.name;
}
}
八、启动HAL
WifiVendorHal.java-->startVendorHal --> HalDeviceManager.java --> startWifi --> IWifi.start
mWifi.start()方法是启动实际加载WiFi动作的调用,这里涉及HIDL机制调用。通过获取IWifi接口对象,调用其方法。这里IWifi接口对象是IWifi.hal文件中实现。
android/hardware/interfaces/wifi/1.0/IWifi.hal
在编译时,编译器会将IWifi.hal解析为IWifi.java文件,直接看该文件中的start方法实现即可。
android/out/soong//.intermediates/hardware/interfaces/wifi/1.0/android.hardware.wifi-V1.0-java_gen_java/gen/srcs/android/hardware/wifi/V1_0/IWifi.java
public android.hardware.wifi.V1_0.WifiStatus start() throws android.os.RemoteException {
try {
... ... ... ...
mRemote.transact(3 /* start */, _hidl_request, _hidl_reply, 0 /* flags */);
_hidl_reply.verifySuccess();
_hidl_request.releaseTemporaryStorage();
return _hidl_out_status;
} finally {
_hidl_reply.release();
}
}
通过binder调用,将调用到wifi.cpp中的start()方法.
android/hardware/interfaces/wifi/1.4/default/wifi.cpp
Return<void> Wifi::start(start_cb hidl_status_cb) {
return validateAndCall(this, WifiStatusCode::ERROR_UNKNOWN,
&Wifi::startInternal, hidl_status_cb);
}
wifi.cpp->start() ==> wifi.cpp->startInternal() ==> wifi.cpp->initializeModeControllerAndLegacyHal()
==> WifiModeController->initialize() ==> DriverTool->LoadDriver()
通过调用DriverTool->LoadDriver将返回到Android framework中。下面是LoadDriver()的实现。
android/frameworks/opt/net/wifi/libwifi_hal/include/wifi_hal/driver_tool.cpp
bool DriverTool::LoadDriver() {
return ::wifi_load_driver() == 0;
}
在wifi_load_driver()方法中,将调用系统接口加载WiFi驱动ko。关于系统insmod接口的调用,本文不做分析。到这里,已梳理完在WifiNative类中调用的startHal()方法。
android/frameworks/opt/net/wifi/libwifi_hal/wifi_hal_common.cpp
int wifi_load_driver() {
... ... ... ...
insmod(file,args);
... ... ... ...
}
调用WifiNl80211Manager类的setupInterfaceForClientMode()方法。
该类的主要对WiFi 80211nl管理接口的封装,接口在WiFicond守护进程中呈现给WiFi框架。该类提供的接口仅使用与WiFi框架,访问权限受selinux权限保护。
setupInterfaceForClientMode()方法主要为Station模式设置接口。
android/frameworks/base/wifi/java/android/net/wifi/nl80211/WifiNl80211Manager.java
public boolean setupInterfaceForClientMode(@NonNull String ifaceName,
@NonNull @CallbackExecutor Executor executor,
@NonNull ScanEventCallback scanCallback, @NonNull ScanEventCallback pnoScanCallback) {
... ... ... ...
// Refresh Handlers
mClientInterfaces.put(ifaceName, clientInterface);
try {
IWifiScannerImpl wificondScanner = clientInterface.getWifiScannerImpl();
mWificondScanners.put(ifaceName, wificondScanner);
Binder.allowBlocking(wificondScanner.asBinder());
ScanEventHandler scanEventHandler = new ScanEventHandler(executor, scanCallback);
mScanEventHandlers.put(ifaceName, scanEventHandler);
wificondScanner.subscribeScanEvents(scanEventHandler);
PnoScanEventHandler pnoScanEventHandler = new PnoScanEventHandler(executor,
pnoScanCallback);
mPnoScanEventHandlers.put(ifaceName, pnoScanEventHandler);
wificondScanner.subscribePnoScanEvents(pnoScanEventHandler);
... ... ... ...
}
到这里,ClientModeStateMachine状态机在IdleState状态成功处理完了CMD_START消息。状态机将转到“mScanOnlyModeState”状态,将会执行以下调用流程(具体原因可查看状态机机制)。
IdleState.exit()->StartedState.enter()->StartedState.exit()->ScanOnlyModeState.enter()。
九、启动HAL以后,就要启动supplicant了。
在第五步的时候我们调用了ActiveModeWarden.java的startClientModeManagerh函数。start以后会执行switchClientModeManagerRole
private boolean switchClientModeManagerRole(@NonNull ClientModeManager modeManager) {
if (mSettingsStore.isWifiToggleEnabled()) {
modeManager.setRole(ActiveModeManager.ROLE_CLIENT_PRIMARY);
} else if (checkScanOnlyModeAvailable()) {
modeManager.setRole(ActiveModeManager.ROLE_CLIENT_SCAN_ONLY);
} else {
Log.e(TAG, "Something is wrong, no client mode toggles enabled");
return false;
}
return true;
n true;
}
十、从上一步可以看出setRole的参数为ROLE_CLIENT_SCAN_ONLY,所以这里发送的是CMD_SWITCH_TO_CONNECT_MODE广播
frameworks/opt/net/wifi/service/java/com/android/server/wifi/ClientModeManager.java
public void setRole(@Role int role) {
Preconditions.checkState(CLIENT_ROLES.contains(role));
if (role == ROLE_CLIENT_SCAN_ONLY) {
mTargetRole = role;
// Switch client mode manager to scan only mode.
mStateMachine.sendMessage(ClientModeStateMachine.CMD_SWITCH_TO_SCAN_ONLY_MODE);
} else if (CLIENT_CONNECTIVITY_ROLES.contains(role)) {
mTargetRole = role;
// Switch client mode manager to connect mode.
mStateMachine.sendMessage(ClientModeStateMachine.CMD_SWITCH_TO_CONNECT_MODE, role);
}
}
十一、看一下CMD_SWITCH_TO_CONNECT_MODE的处理,这里先执行了switchClientInterfaceToConnectivityMode
private class StartedState extends State {
public boolean processMessage(Message message) {
switch(message.what) {
case CMD_SWITCH_TO_CONNECT_MODE:
mRole = message.arg1; // could be any one of possible connect mode roles.
updateConnectModeState(WifiManager.WIFI_STATE_ENABLING,
WifiManager.WIFI_STATE_DISABLED);
if (!mWifiNative.switchClientInterfaceToConnectivityMode(
mClientInterfaceName)) {
updateConnectModeState(WifiManager.WIFI_STATE_UNKNOWN,
WifiManager.WIFI_STATE_ENABLING);
updateConnectModeState(WifiManager.WIFI_STATE_DISABLED,
WifiManager.WIFI_STATE_UNKNOWN);
mModeListener.onStartFailure();
break;
}
transitionTo(mConnectModeState);
break;
十二、可以看到这里启动了supplicant
frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiNative.java
public boolean switchClientInterfaceToConnectivityMode(@NonNull String ifaceName) {
synchronized (mLock) {
final Iface iface = mIfaceMgr.getIface(ifaceName);
if (!startSupplicant()) {
Log.e(TAG, "Failed to start supplicant");
teardownInterface(iface.name);
mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToSupplicant();
return false;
}
if (!mSupplicantStaIfaceHal.setupIface(iface.name)) {
Log.e(TAG, "Failed to setup iface in supplicant on " + iface);
teardownInterface(iface.name);
mWifiMetrics.incrementNumSetupClientInterfaceFailureDueToSupplicant();
return false;
}
iface.type = Iface.IFACE_TYPE_STA_FOR_CONNECTIVITY;
iface.featureSet = getSupportedFeatureSetInternal(iface.name);
Log.i(TAG, "Successfully switched to connectivity mode on iface=" + iface);
return true;
}
}
private boolean startSupplicant() {
synchronized (mLock) {
if (!mIfaceMgr.hasAnyStaIfaceForConnectivity()) {
if (!startAndWaitForSupplicantConnection()) {
Log.e(TAG, "Failed to connect to supplicant");
return false;
}
if (!mSupplicantStaIfaceHal.registerDeathHandler(
new SupplicantDeathHandlerInternal())) {
Log.e(TAG, "Failed to register supplicant death handler");
return false;
}
}
return true;
}
}
在这里等待与supplicant建立连接
private boolean startAndWaitForSupplicantConnection() {
// Start initialization if not already started.
if (!mSupplicantStaIfaceHal.isInitializationStarted()
&& !mSupplicantStaIfaceHal.initialize()) {
return false;
}
if (!mSupplicantStaIfaceHal.startDaemon()) {
Log.e(TAG, "Failed to startup supplicant");
return false;
}
boolean connected = false;
int connectTries = 0;
while (!connected && connectTries++ < CONNECT_TO_SUPPLICANT_RETRY_TIMES) {
// Check if the initialization is complete.
connected = mSupplicantStaIfaceHal.isInitializationComplete();
if (connected) {
break;
}
try {
Thread.sleep(CONNECT_TO_SUPPLICANT_RETRY_INTERVAL_MS);
} catch (InterruptedException ignore) {
}
}
return connected;
}
十三、这里是通过HIDL来打开supplicant的
frameworks/opt/net/wifi/service/java/com/android/server/wifi/SupplicantStaIfaceHal.java
startDaemon -> startDaemon_V1_1 -> getSupplicantMockableV1_1 -> getSupplicantMockable
protected ISupplicant getSupplicantMockable() throws RemoteException, NoSuchElementException {
synchronized (mLock) {
ISupplicant iSupplicant = ISupplicant.getService();
if (iSupplicant == null) {
throw new NoSuchElementException("Cannot get root service.");
}
return iSupplicant;
}
}
android/out/soong/intermediates/hardware/interfaces/wifi/supplicant/1.0/android.hardware.wifi.supplicant-V1.0-java_gen_java/gen/srcs/android/hardware/wifi/supplicant/V1_0/ISupplicant.java
public static ISupplicant getService(String serviceName) throws android.os.RemoteException {
return ISupplicant.asInterface(android.os.HwBinder.getService("[email protected]::ISupplicant", serviceName));
}
十四、 在这个方法中将触发启动wpa_supplicant进程,这里需要注意,在manifest.xml中对其需要进行配置,运行时会将服务名称注册到hwservicemanager中。
wpa_supplicant目录下文件调用:
main.c ==> wpa_supplicant.c->wpa_supplicant_init() ==> notify.c->wpas_notify_supplicant_initialized() ==> hidl.cpp->wpas_hidl_init() ==> Hidl_manager.cpp->registerHidlService()
int HidlManager::registerHidlService(struct wpa_global *global)
{
// Create the main hidl service object and register it.
supplicant_object_ = new Supplicant(global);
if (supplicant_object_->registerAsService("wpa_supplicant") != android::NO_ERROR) {
return 1;
}
return 0;
}
十五、将wpa_supplicant添加注册到hwservicemanager,SupplicantStaIfaceHal.getSupplicantMockable()执行完成返回。
这里再深入看下“supplicant_object_->registerAsService(“wpa_supplicant”)”是如何通过调用注册的呢?
android/out/soong/.intermediates/hardware/interfaces/wifi/supplicant/1.3/[email protected]_genc++/gen/android/hardware/wifi/supplicant/1.3/SupplicantAll.cpp
android/system/libhidl/transport/ServiceManagement.cpp
android/system/hwservicemanager/ServiceManager.cpp
supplicant_object_->registerAsService("wpa_supplicant") ==> ISupplicant.hal
==> ISupplicantAll.cpp->registerAsService()
==> ::android::hardware::details::registerAsServiceInternal(this, serviceName)
==> ServiceManagement.cpp->registerAsServiceInternal()
==> ServiceManager->addWithChain()
==> ServiceManager->addImpl()
十六、wpa_supplicant注册完成后,SupplicantStaIfaceHal类中将收到回调通知信息,
private final IServiceNotification mServiceNotificationCallback =
new IServiceNotification.Stub() {
public void onRegistration(String fqName, String name, boolean preexisting) {
synchronized (mLock) {
if (!initSupplicantService()) {
supplicantServiceDiedHandler(mDeathRecipientCookie);
}
返回通知的调用逻辑。
SupplicantStaIfaceHal.initSupplicantService() -> SupplicantStaIfaceHal.getSupplicantMockable()
十七、到此位置supplicant已经启动。
switchClientInterfaceToConnectivityMode会继续调用SupplicantStaIfaceHal.setupIface()方法设置接口。设置成功后,就会打印成功的日志。
Log.i(TAG, "Successfully switched to connectivity mode on iface=" + iface);
十八、CMD_SWITCH_TO_CONNECT_MODE消息处理完以后状态机就会切换到ConnectModeState。
这里会调用setOperationalMode
transitionTo(mConnectModeState);
private class ConnectModeState extends State {
@Override
public void enter() {
Log.d(TAG, "entering ConnectModeState");
mClientModeImpl.registerModeListener(mClientModeImplListener);
mClientModeImpl.setOperationalMode(ClientModeImpl.CONNECT_MODE,
mClientInterfaceName);
}
十九、这里会进入到mDisconnectedState
frameworks/opt/net/wifi/service/java/com/android/server/wifi/ClientModeImpl.java
public void setOperationalMode(int mode, String ifaceName) {
if (mVerboseLoggingEnabled) {
log("setting operational mode to " + String.valueOf(mode) + " for iface: " + ifaceName);
}
mModeChange = true;
if (mode != CONNECT_MODE) {
// we are disabling client mode... need to exit connect mode now
transitionTo(mDefaultState);
} else {
// do a quick sanity check on the iface name, make sure it isn't null
if (ifaceName != null) {
mInterfaceName = ifaceName;
updateInterfaceCapabilities(ifaceName);
transitionTo(mDisconnectedState);
mWifiScoreReport.setInterfaceName(ifaceName);
} else {
Log.e(TAG, "supposed to enter connect mode, but iface is null -> DefaultState");
transitionTo(mDefaultState);
}
}
// use the CMD_SET_OPERATIONAL_MODE to force the transitions before other messages are
// handled.
sendMessageAtFrontOfQueue(CMD_SET_OPERATIONAL_MODE);
}
二十、ActiveModeWarden类中设置的ClientLister将被触发回调。 wifiScaner.setScanningEnabled()发送消息CMD_ENABLE,给到WiFiscanningSerivceimpl类中。
到这里,WiFi已处于打开状态,并将进行扫描网络,待连接。WiFi打开流程分析完成。
ActiveModeWarden.ClientListener -> ScanRequestProxy.enableScanning() ->
ScanRequestProxy.enableScanningInternal() -> wifiScaner.setScanningEnabled()