代码基于Android P
Android使用wpa_supplicant作为daemon来控制WIFI,它是一个安全中间件,代码位于external/wpa_supplicant,为各种无线网卡提供统一的安全机制。当然在这里只是介绍一下wpa_supplicant和 hostapd,研究分析的部分主要还是应用层和java框架层,有时也会涉及Native层。
wpa_supplicant_8主要有三个子目录 :
- hostapd:当手机进入Soft AP模式时,手机将扮演AP的角色,需要hostapd来提供AP的功能,也就是wifi热点的实现。
- wpa_supplicant:Station模式,也叫Managed模式,这是平时最常见的使用wifi连接AP的情况。
- src:hostapd和wpa_supplicant中都包含一些通用的数据结构和处理方法,这些内容都放在此src目录中。
Hostapd简介
hostapd能够使得无线网卡切换为master模式,模拟AP(通常可以认为是路由器)功能,也就是我们说的软AP(Soft AP)。
Hostapd的功能就是作为AP的认证服务器,负责控制管理stations(通常可以认为带无线网卡的PC)的接入和认证。
通过Hostapd可以将无线网卡切换为AP/Master模式,通过修改配置文件,可以建立一个开放式的(不加密)的,WEP,WPA或WPA2的无线网络。并且通过修改配置文件可以设置无线网卡的各种参数,包括频率,信号,beacon包时间间隔,是否发送beacon包,如果响应探针请求等等。
当用以wifi的时候,作为station,工作在managed模式,有后台进程wpa_supplicant对认证管理。
工作在sofap的时候,作为AP, 工作在master模式,由后台进程hostapd做认证处理。
作为station,用户通过UI启动WIFI,由framework层,JNI,hardware层加载WIFI的驱动,加载firmware。驱动加载成功后,启动wpa_supplicant,设置mad地址,设置为managed模式,通过ioctl命令启动scan,启动DHCPD获取IP地址,链接到对应的AP,建立连接后同时sock通信。
作为softap模式的时候,和station模式的流程差不多,只不过不是启动wpa_supplicant,启动hostapd的后台管理进程。
由framework层,JNI,hardware层加载softap的驱动,加载firmware。驱动加载成功后,启动hostap,设置mad地址,设置为master模式,设置BSSID,启动DHCPD获取IP地址。等待station来连接。
流程图
Android P之后,Wifi模块增加了packages/apps/Settings/src/com/android/settings/wifi/tether/路径,相当于把Wifi热点独立放到了tether文件夹下面,并添加了WifiTetherSettings.java类,对应着Wifi热点的界面,而Android P之前是没有的,Wifi热点界面之前是对应在TetherSettings的一部分,有些厂商也还是会改到TetherSettings上。
1.1 创建WifiTetherSwitchBarController
1 2 3 4 5 6 7 8 9 10 11 12 13 | //packages/apps/Settings/src/com/android/settings/wifi/tether/WifiTetherSettings.java @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // Assume we are in a SettingsActivity. This is only safe because we currently use // SettingsActivity as base for all preference fragments. final SettingsActivity activity = (SettingsActivity) getActivity(); final SwitchBar switchBar = activity.getSwitchBar(); mSwitchBarController = new WifiTetherSwitchBarController(activity, new SwitchBarController(switchBar)); getLifecycle().addObserver(mSwitchBarController); switchBar.show(); } |
1.2 初始化mSwitchBarController
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 | //packages/apps/Settings/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java public class WifiTetherSwitchBarController implements SwitchWidgetController.OnSwitchChangeListener, LifecycleObserver, OnStart, OnStop, DataSaverBackend.Listener { //... } //packages/apps/Settings/src/com/android/settings/widget/SwitchWidgetController.java public interface OnSwitchChangeListener { /** * Called when the checked state of the Switch has changed. * * @param isChecked The new checked state of switchView. * * @return true to update the state of the switch with the new value. */ boolean onSwitchToggled(boolean isChecked); } //packages/apps/Settings/src/com/android/settings/wifi/tether/WifiTetherSwitchBarController.java public boolean onSwitchToggled(boolean isChecked) { if (!isChecked) { stopTether(); } else if (!mWifiManager.isWifiApEnabled()) { startTether(); } return true; } void startTether() { mSwitchBar.setEnabled(false); mConnectivityManager.startTethering(TETHERING_WIFI, true /* showProvisioningUi */, mOnStartTetheringCallback, new Handler(Looper.getMainLooper())); } |
1.3 启动 mConnectivityManager.startTethering
注意: android O开始通过mConnectivityManager.startTethering来启动热点了,之前都是通过WifiManager的setWifiApEnable的方法,该方法现在也已废弃。
1 2 3 4 5 6 7 8 9 10 11 12 13 | //frameworks/base/core/java/android/net/ConnectivityManager.java /** * Convenient overload for * {@link #startTethering(int, boolean, OnStartTetheringCallback, Handler)} which passes a null * handler to run on the current thread's {@link Looper}. * @hide */ @SystemApi @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int type, boolean showProvisioningUi, final OnStartTetheringCallback callback) { startTethering(type, showProvisioningUi, callback, null); } |
1.4 startTethering
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 | /** * Runs tether provisioning for the given type if needed and then starts tethering if * the check succeeds. If no carrier provisioning is required for tethering, tethering is * enabled immediately. If provisioning fails, tethering will not be enabled. It also * schedules tether provisioning re-checks if appropriate. * * @param type The type of tethering to start. Must be one of * {@link ConnectivityManager.TETHERING_WIFI}, * {@link ConnectivityManager.TETHERING_USB}, or * {@link ConnectivityManager.TETHERING_BLUETOOTH}. * @param showProvisioningUi a boolean indicating to show the provisioning app UI if there * is one. This should be true the first time this function is called and also any time * the user can see this UI. It gives users information from their carrier about the * check failing and how they can sign up for tethering if possible. * @param callback an {@link OnStartTetheringCallback} which will be called to notify the caller * of the result of trying to tether. * @param handler {@link Handler} to specify the thread upon which the callback will be invoked. * @hide */ @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); } } |
1.5 mService.startTethering
1 2 3 4 5 6 7 8 9 10 11 12 | //frameworks/base/services/core/java/com/android/server/ConnectivityService.java @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); } |
1.6 mTethering.startTethering
1 2 3 4 5 6 7 8 9 10 11 12 13 | //frameworks/base/services/core/java/com/android/server/connectivity/Tethering.java public void startTethering(int type, ResultReceiver receiver, boolean showProvisioningUi) { if (!isTetherProvisioningRequired()) { enableTetheringInternal(type, true, receiver); return; } if (showProvisioningUi) { runUiTetherProvisioningAndEnable(type, receiver); } else { runSilentTetherProvisioningAndEnable(type, receiver); } } |
1.7 Tethering.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 26 27 28 29 30 31 | /** * Enables or disables tethering for the given type. This should only be called once * provisioning has succeeded or is not necessary. It will also schedule provisioning rechecks * for the specified interface. */ private void enableTetheringInternal(int type, boolean enable, ResultReceiver receiver) { boolean isProvisioningRequired = enable && isTetherProvisioningRequired(); int result; switch (type) { case TETHERING_WIFI: result = setWifiTethering(enable); if (isProvisioningRequired && result == TETHER_ERROR_NO_ERROR) { scheduleProvisioningRechecks(type); } sendTetherResult(receiver, result); break; case TETHERING_USB: result = setUsbTethering(enable); if (isProvisioningRequired && result == TETHER_ERROR_NO_ERROR) { scheduleProvisioningRechecks(type); } sendTetherResult(receiver, result); break; case TETHERING_BLUETOOTH: setBluetoothTethering(enable, receiver); break; default: Log.w(TAG, "Invalid tether type."); sendTetherResult(receiver, TETHER_ERROR_UNKNOWN_IFACE); } } |
1.8 Tethering.setWifiTethering
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | private int setWifiTethering(final boolean enable) { int rval = TETHER_ERROR_MASTER_ERROR; final long ident = Binder.clearCallingIdentity(); try { synchronized (mPublicSync) { mWifiTetherRequested = enable; final WifiManager mgr = getWifiManager(); if ((enable && mgr.startSoftAp(null /* use existing wifi config */)) || (!enable && mgr.stopSoftAp())) { rval = TETHER_ERROR_NO_ERROR; } } } finally { Binder.restoreCallingIdentity(ident); } return rval; } |
1.9 mgr.startSoftAp
1 2 3 4 5 6 7 8 | //frameworks/base/wifi/java/android/net/wifi/WifiManager.java public boolean startSoftAp(@Nullable WifiConfiguration wifiConfig) { try { return mService.startSoftAp(wifiConfig); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } |
startSoftAp --> startSoftApInternal --> mWifiController.sendMessage --> makeSoftApManager-->
frameworks/opt/net/wifi/service/java/com/android/server/wifi/SoftApManager.java
frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiInjector.java
1.10 SoftApStateMachine是SoftApManager的内部类。
函数中调用Native的 stopSoftAp 将通过Native调用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 31 32 33 34 35 36 37 38 39 40 | @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; } } |
1.11 mWifiNative.startSoftAp
mWifiNative.startSoftAp
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 | /** * Start a soft AP instance with the given configuration. * @param config AP configuration * @return integer result code */ private int startSoftAp(WifiConfiguration config) { if (config == null || config.SSID == null) { Log.e(TAG, "Unable to start soft AP without valid configuration"); return ERROR_GENERIC; } // Make a copy of configuration for updating AP band and channel. WifiConfiguration localConfig = new WifiConfiguration(config); int result = ApConfigUtil.updateApChannelConfig( mWifiNative, mCountryCode, mWifiApConfigStore.getAllowed2GChannel(), localConfig); if (result != SUCCESS) { Log.e(TAG, "Failed to update AP band and channel"); return result; } // Setup country code if it is provided. if (mCountryCode != null) { // Country code is mandatory for 5GHz band, return an error if failed to set // country code when AP is configured for 5GHz band. if (!mWifiNative.setCountryCodeHal( mApInterfaceName, mCountryCode.toUpperCase(Locale.ROOT)) && config.apBand == WifiConfiguration.AP_BAND_5GHZ) { Log.e(TAG, "Failed to set country code, required for setting up " + "soft ap in 5GHz"); return ERROR_GENERIC; } } if (localConfig.hiddenSSID) { Log.d(TAG, "SoftAP is a hidden network"); } if (!mWifiNative.startSoftAp(mApInterfaceName, localConfig, mSoftApListener)) { Log.e(TAG, "Soft AP start failed"); return ERROR_GENERIC; } Log.d(TAG, "Soft AP is started"); return SUCCESS; } |
1.12 mWifiNative.startSoftAp
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 | //frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiNative.java /** * Start Soft AP operation using the provided configuration. * * @param ifaceName Name of the interface. * @param config Configuration to use for the soft ap created. * @param listener Callback for AP events. * @return true on success, false otherwise. */ public boolean startSoftAp( @NonNull String ifaceName, WifiConfiguration config, SoftApListener listener) { if (!mWificondControl.startHostapd(ifaceName, listener)) { Log.e(TAG, "Failed to start hostapd"); mWifiMetrics.incrementNumSetupSoftApInterfaceFailureDueToHostapd(); return false; } if (!waitForHostapdConnection()) { Log.e(TAG, "Failed to establish connection to hostapd"); mWifiMetrics.incrementNumSetupSoftApInterfaceFailureDueToHostapd(); return false; } if (!mHostapdHal.registerDeathHandler(new HostapdDeathHandlerInternal())) { Log.e(TAG, "Failed to register hostapd death handler"); mWifiMetrics.incrementNumSetupSoftApInterfaceFailureDueToHostapd(); return false; } if (!mHostapdHal.addAccessPoint(ifaceName, config)) { Log.e(TAG, "Failed to add acccess point"); mWifiMetrics.incrementNumSetupSoftApInterfaceFailureDueToHostapd(); return false; } return true; } |
以上函数处理了
- mWificondControl.startHostapd
- waitForHostapdConnection
- mHostapdHal.registerDeathHandler
- mHostapdHal.addAccessPoint
就以mWificondControl.startHostapd为主线进行分析。
1.13 mWificondControl.startHostapd
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | //frameworks/opt/net/wifi/service/java/com/android/server/wifi/WificondControl.java public boolean startHostapd(@NonNull String ifaceName, SoftApListener listener) { IApInterface iface = getApInterface(ifaceName); if (iface == null) { Log.e(TAG, "No valid ap interface handler"); return false; } try { IApInterfaceEventCallback callback = new ApInterfaceEventCallback(listener); mApInterfaceListeners.put(ifaceName, callback); boolean success = iface.startHostapd(callback); if (!success) { Log.e(TAG, "Failed to start hostapd."); return false; } } catch (RemoteException e) { Log.e(TAG, "Exception in starting soft AP: " + e); return false; } return true; } |
1.14 iface.startHostapd
1 2 3 4 5 6 7 | //system/connectivity/wificond/aidl/android/net/wifi/IApInterface.aidl boolean startHostapd(IApInterfaceEventCallback callback); //system/connectivity/wificond/ap_interface_binder.h binder::Status startHostapd( const sp<net::wifi::IApInterfaceEventCallback>& callback, bool* out_success) override; |
1.15 ApInterfaceBinder::startHostapd
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | //system/connectivity/wificond/ap_interface_binder.cpp binder::Status ApInterfaceBinder::startHostapd( const sp<IApInterfaceEventCallback>& callback, bool* out_success) { *out_success = false; if (!impl_) { LOG(WARNING) << "Cannot start hostapd on dead ApInterface."; return binder::Status::ok(); } *out_success = impl_->StartHostapd(); if (*out_success) { ap_interface_event_callback_ = callback; } return binder::Status::ok(); } |
1.16 impl_->StartHostapd()
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 | //system/connectivity/wificond/ap_interface_impl.h #ifndef WIFICOND_AP_INTERFACE_IMPL_H_ #define WIFICOND_AP_INTERFACE_IMPL_H_ #include <string> #include <vector> #include <android-base/macros.h> #include <wifi_system/hostapd_manager.h> #include <wifi_system/interface_tool.h> #include "wificond/net/netlink_manager.h" #include "android/net/wifi/IApInterface.h" namespace android { namespace wificond { class ApInterfaceBinder; class NetlinkUtils; // Holds the guts of how we control network interfaces capable of exposing an AP // via hostapd. Because remote processes may hold on to the corresponding // binder object past the lifetime of the local object, we are forced to // keep this object separate from the binder representation of itself. class ApInterfaceImpl { public: ApInterfaceImpl(const std::string& interface_name, uint32_t interface_index, NetlinkUtils* netlink_utils, wifi_system::InterfaceTool* if_tool, wifi_system::HostapdManager* hostapd_manager); ~ApInterfaceImpl(); // Get a pointer to the binder representing this ApInterfaceImpl. android::sp<android::net::wifi::IApInterface> GetBinder() const; bool StartHostapd(); bool StopHostapd(); std::string GetInterfaceName() { return interface_name_; } int GetNumberOfAssociatedStations() const; void Dump(std::stringstream* ss) const; private: const std::string interface_name_; const uint32_t interface_index_; NetlinkUtils* const netlink_utils_; wifi_system::InterfaceTool* const if_tool_; wifi_system::HostapdManager* const hostapd_manager_; const android::sp<ApInterfaceBinder> binder_; // Number of associated stations. int number_of_associated_stations_; void OnStationEvent(StationEvent event, const std::vector<uint8_t>& mac_address); void OnChannelSwitchEvent(uint32_t frequency, ChannelBandwidth bandwidth); DISALLOW_COPY_AND_ASSIGN(ApInterfaceImpl); }; } // namespace wificond } // namespace android #endif // WIFICOND_AP_INTERFACE_IMPL_H_ |
1.17 ApInterfaceImpl::StartHostapd()
1 2 3 4 | //system/connectivity/wificond/ap_interface_impl.cpp bool ApInterfaceImpl::StartHostapd() { return hostapd_manager_->StartHostapd(); } |
总结: 工作在sofap的时候,作为AP, 工作在master模式,由后台进程hostapd做认证处理。
完~~~
文 | 力卉编程