本篇文章梳理三件事:
① wifi列表扫描与更新机制
② wifi连接实现与现状
③ wifi开关实现与现状
源码分析基于:android-13.0.0_r43
一、wifi列表扫描与更新机制
1.1 整体架构
应用层 (Application Layer)
└── 用户应用
|
框架层 (Framework Layer) wifi扫描结果集:ScanRequestProxy的Map<String, ScanResult> mLastScanResultsMap中
└── WifiManager
└── WifiServiceImpl (IWifiManager.Stub)
└── WifiScanningServiceImpl (IWifiScanner.Stub)
└── WifiNative
|
硬件抽象层 (HAL)
└── IWifiScannerImpl (WificondScannerImpl、HalWifiScannerImpl)
|
用户空间守护进程 (User Space Daemon)
└── wificond
| (netlink通信)
内核层 (Kernel Layer)
└── WiFi 驱动(wpa_supplicant)
① 扫描流程
应用层通过IWifiManager#startScan binder call到system framework发起扫描请求,在IWifiScanner服务内完成整体数据封装及状态机扭转,根据状态的适合时机调用WifiNative与Hal通信,HAL 层接口实现将请求转发给 wificond(native 守护进程),由他收到请求并进行具体的操作,通过netlink与内核通信;
② 更新流程
内核将扫描结果返回给wificond,wificond从NL80211Packet套接字里取出数据,通过handle层层回调上传,最终更新framework层的ScanRequestProxy的Map<String, ScanResult> mLastScanResultsMap中。
1.2 wifi扫描触发机制
1)主动触发
app调用系统api,例如:IWifiManager#startScan、IWifiScanner#startScan 、ConnectivityManager#requestNetwork(发起网络连接弹窗前,会主动先触发一次wifi扫描,且不管成功失败都会循环扫描,直到用户点击确定连接)等。
2)周期触发
① 前置条件:WIFI开启 & 亮屏 & 未连接wifi & 自动开启WLAN(默认开启)
② 触发和重置事件:亮灭屏、网络状态切换(事件触发会检查前置条件)
③ 扫描频率:
- 在wifi设置页:固定10S一次(WifiTracker#onStart -> resumeScanning -> Scanner.resume);
- 在非wifi设置页:20-160S一次(WifiConnectivityManager#startConnectivityScan --> startPeriodicScan --> startPeriodicSingleScan);
1.3 wifi扫描更新详细流程梳理
以starScan为入口分析整体wifi列表扫描更新流程:
1.3.1 扫描流程
关键步骤总结:
① 应用层通过WifiManager#startScan发起扫描请求;
② 中间通过服务接口IWifiManager实现类WifiServiceImpl处理扫描请求;
③ 再交给服务接口IWifiScanner实现类WifiScanningServiceImpl 进一步处理请求,该服务更垂直于 扫描任务,内部通过状态机管理扫描任务,同时调用底层接口进行扫描;
④ WifiScanningServiceImpl通过WifiNative调用HAL层;
⑤ HAL 层接口实现将请求转发给 wificond(native 守护进程),由他收到请求并进行具体的操作,与内核通信;
⑥ 内核层 WiFi 驱动处理扫描并返回结果 HAL通过与驱动通信,最终触发硬件进行实际Wifi扫描。
1.3.2 更新流程
关键步骤总结:
① 内核驱动完成wifi扫描后,通过 Netlink 消息将结果返回给用户空间;
② 扫描结果通过handler层层向上传递,最终到WifiScanner,由他回调扫描监听,更新ScanRequestProxy中的扫描结果缓存Map
二、wifi连接实现与现状
方案1:WifiManager#addNetwork & enableNetwork
结论:< android Q版本可静默连接, >= android Q版本 被限制,额外需要android.permission.SYSTEM_ALERT_WINDOW。
1)权限:
android.permission.CHANGE_WIFI_STATE
android.permission.SYSTEM_ALERT_WINDOW
2)代码实现:
WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
WifiConfiguration config = new WifiConfiguration();
config.SSID = String.format("\"%s\"", WIFI_SSID);
config.preSharedKey = String.format("\"%s\"", WIFI_PASSWORD);
try {
int netId = wifiManager.addNetwork(config);
boolean enableNetwork = wifiManager.enableNetwork(netId, true);
} catch (Throwable e) {
Log.i(TAG, "Failed to add network: " + e.getMessage());
}
3)源码分析
调用栈:
① 添加网络
android.net.wifi.WifiManager#addNetwork
com.android.server.wifi.WifiServiceImpl#addOrUpdateNetwork // 条件限制:版本 系统应用 or 悬浮窗权限
com.android.server.wifi.WifiServiceImpl#addOrUpdateNetworkInternal
com.android.server.wifi.WifiConfigManager#addOrUpdateNetwork
com.android.server.wifi.WifiConfigManager#addOrUpdateNetworkInternal
添加网络最终加到ConfigurationMap中,并经由com.android.server.wifi.NetworkListStoreData持久化缓存在:/data/misc/apexdata/com.android.wifi.WifiConfigStore.xml里
② 连接网络
android.net.wifi.WifiManager#enableNetwork
com.android.server.wifi.WifiServiceImpl#enableNetwork // 条件限制:版本 系统应用 or 悬浮窗权限
com.android.server.wifi.WifiServiceImpl#triggerConnectAndReturnStatus
com.android.server.wifi.ConnectHelper#connectToNetwork
com.android.server.wifi.ConcreteClientModeManager#connectNetwork
com.android.server.wifi.ClientModeImpl#connectNetwork
send Message: CMD_CONNECT_NETWORK
com.android.server.wifi.ClientModeImpl#connectToUserSelectNetwork
com.android.server.wifi.ClientModeImpl#startConnectToNetwork
com.android.server.wifi.ClientModeImpl#connectToNetwork(WifiConfiguration config)
com.android.server.wifi.WifiNative#connectToNetwork(String ifaceName, WifiConfiguration configuration)
com.android.server.wifi.SupplicantStaIfaceHal#connectToNetwork
com.android.server.wifi.SupplicantStaNetworkHalAidlImpl#select
hardware/interfaces/wifi/supplicant/1.0/ISupplicantStaNetwork.hal#select
最终将提供的网络配置添加到wpa_supplicant并启动连接
① WifiManager#addNetwork 和 #enableNetwork 限制条件:小于Q版本、系统应用或特权应用、悬浮窗权限。
mWifiPermissionsUtil.isTargetSdkLessThan:applicationInfo targetSdkVersion
isPrivileged:系统权限: checkPermission(android.Manifest.permission.NETWORK_SETTINGS checkPermission(android.Manifest.permission.NETWORK_SETUP_WIZARD checkPermission(android.Manifest.permission.NETWORK_STACK checkPermission(android.Manifest.permission.NETWORK_MANAGED_PROVISIONING mContext.getPackageManager().checkSignatures(i, 1000) == 0; 即:uid = 1000
isDeviceOrProfileOwner(callingUid, str): DeviceOwner: 设备所有者。android 5.0 引入,开放DevicePolicyManager更敏感的API能力。一个设备只能存在一个DeviceOwner,DeviceOwner应用不能被卸载,也无法在设置->安全中关闭权限, 生产环境没有好的静激活方式。 ProfileOwner:配置管理者,具有配置和管理设备用户配置文件(profile)的权限。实现需要系统权限:android.permission.MANAGE_USERS
mWifiPermissionsUtil.isSystem(str, callingUid): applicationInfo flag = ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP
② addNetwork:添加网络 最终会内存缓存在ConfigurationMap中,并且持久化缓存在:/data/misc/apexdata/com.android.wifi.WifiConfigStore.xml里,过程中暂时没有额外可干预添加的点;
③ enableNetwork: 从map中获取对应网络config,最终将其添加到wpa_supplicant并启动连接,过程中暂时没有额外可干预添加的点。
方案2:ConnectivityManager#requestNetwork
结论:>= android Q版本可以使用, 没有额外限制,唯一问题就是会拉起Settings弹窗,用户交互点击连接,再实现连接,过程并不静默。
1)权限
android.permission.CHANGE_NETWORK_STATE
- 代码实现
@RequiresApi(api = Build.VERSION_CODES.Q)
public void requestNetwork(Context context) {
// 构建 NetworkRequest 对象
NetworkRequest networkRequest = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.setNetworkSpecifier(new WifiNetworkSpecifier.Builder().setSsid(WIFI_SSID).setWpa2Passphrase(WIFI_PASSWORD).build())
.build();
// 获取 ConnectivityManager 实例
ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback() {
@Override
public void onAvailable(Network network) {
}
@Override
public void onUnavailable() {
}
@Override
public void onLost(Network network) {
}
};
// 注册网络回调
connectivityManager.requestNetwork(networkRequest, networkCallback);
}
3)源码分析
调用栈:
① system_server ConnectivityService#requestNetwork调用栈:
com.android.server.ConnectivityService#requestNetwork // type:REQUEST android.permission.CHANGE_NETWORK_STATE权限校验
com.android.server.ConnectivityService#getNriToRegister
com.android.server.ConnectivityService#handleRegisterNetworkRequest // 注册网络请求
com.android.server.ConnectivityService#rematchAllNetworksAndRequests // 评估所有网络请求与可用网络之间的匹配
com.android.server.ConnectivityService#issueNetworkNeeds()
com.android.server.ConnectivityService#informOffer
android.net.NetworkProvider.NetworkOfferCallbackProxy#onNetworkNeeded
com.android.wifi.x.android.net.NetworkFactoryImpl.handleAddRequest //添加网络请求
com.android.server.wifi.WifiNetworkFactory.needNetworkFor
com.android.server.wifi.WifiNetworkFactory#startUi // 拉起com.android.settings.wifi.NetworkRequestDialogActivity
② com.android.settings 弹窗交互:
onResume注册WifiManager.NetworkRequestMatchCallback
呼起Dialog,onClickRescanButton 回调 com.android.server.wifi.WifiNetworkFactory.NetworkFactoryUserSelectionCallback#select
③ system_server WifiNetworkFactory#handleConnectToNetworkUserSelection调用栈:
com.android.server.wifi.WifiNetworkFactory#handleConnectToNetworkUserSelection // 针对用户选择的网络发起连接
com.android.server.wifi.WifiNetworkFactory#handleConnectToNetworkUserSelectionInternal
com.android.server.wifi.ActiveModeWarden#requestLocalOnlyClientModeManager
com.android.server.wifi.WifiNetworkFactory.ClientModeManagerRequestListener#onAnswer
com.android.server.wifi.WifiNetworkFactory#handleClientModeManagerRetrieval
com.android.server.wifi.WifiNetworkFactory#connectToNetwork{
...
com.android.server.wifi.WifiNetworkFactory#addNetworkToWifiConfigManager //添加WiFi
- WifiConfigManager.addOrUpdateNetwork
...
com.android.server.wifi.ConnectHelper#connectToNetwork //连接WIFI
}
附:
ActiveModeWarden
/ \
/ \
ConcreteClientModeManager DefaultClientModeManager
(Client Mode + Scan Only Mode) (Wifi off)
/ \
/ \
ClientModeImpl ScanOnlyModeImpl
方案3:WifiManager#addNetworkSuggestions
结论:只做建议添加,具体连接谁看系统。
1)权限
android.permission.CHANGE_WIFI_STATE
2)代码实现
WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
WifiNetworkSuggestion suggestion = new WifiNetworkSuggestion.Builder()
.setSsid(WIFI_SSID)
.setWpa2Passphrase(WIFI_PASSWORD)
.setIsAppInteractionRequired(true)
.build();
List<WifiNetworkSuggestion> suggestionsList = new ArrayList<>();
suggestionsList.add(suggestion);
int status = wifiManager.addNetworkSuggestions(suggestionsList);
3)源码分析
调用栈
① 添加建议网络
com.android.server.wifi.WifiServiceImpl#addNetworkSuggestions
com.android.server.wifi.WifiNetworkSuggestionsManager#add
② 弹窗确认
com.android.server.wifi.WifiNetworkSuggestionsManager#sendUserApprovalDialog
com.android.server.wifi.WifiDialogManager#createSimpleDialog
com.android.server.wifi.WifiDialogManager.DialogHandle#launchDialog
com.android.settings.wifi.WifiDialogActivity#onCreate
③建立连接
com.android.settings.wifi.WifiDialog.WifiDialogListener#onSubmit
@SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.NETWORK_SETTINGS,
android.Manifest.permission.NETWORK_SETUP_WIZARD,
android.Manifest.permission.NETWORK_STACK
})
android.net.wifi.WifiManager#connect(int, android.net.wifi.WifiManager.ActionListener)
// 弹窗点击取消或者确认后,弹窗不会再出现。
方案4:action: android.settings.WIFI_ADD_NETWORKS
拉起:com.android.settings.wifi.addappnetworks.AddAppNetworksActivity
1)内部实现分析:最终连接使用的系统api:
android.net.wifi.WifiManager#connect(WifiConfiguration, android.net.wifi.WifiManager.ActionListener)
@SystemApi
@RequiresPermission(anyOf = {
android.Manifest.permission.NETWORK_SETTINGS, // signature
android.Manifest.permission.NETWORK_SETUP_WIZARD, // signature|setup
android.Manifest.permission.NETWORK_STACK //signature
})
public void connect(@NonNull WifiConfiguration config, @Nullable ActionListener listener) {
if (config == null) throw new IllegalArgumentException("config cannot be null");
connectInternal(config, WifiConfiguration.INVALID_NETWORK_ID, listener);
}
2)WifiManager#connect的底层调用栈:
android.net.wifi.WifiManager#connect
android.net.wifi.WifiManager#connectInternal
com.android.server.wifi.WifiServiceImpl#connect {
...
com.android.server.wifi.WifiConfigManager#addOrUpdateNetwork(WifiConfiguration, int) //添加
...
com.android.server.wifi.ConnectHelper#connectToNetwork(ClientModeManager, NetworkUpdateResult, ActionListenerWrapper, int, String) //连接
}
方案5:action:android.settings.panel.action.WIFI
com.android.settings/com.android.settings.panel.SettingsPanelActivity 仅仅拉起wifi面板,需要用户主动去连接wifi
汇集目前所有能实现wifi连接的方案,总结:
wifi连接整体分两步:1)添加wifi;2)连接wifi。
添加wifi所有方案都收敛于:WifiConfigManager#addOrUpdateNetwork(WifiConfiguration config, int uid);
连接wifi所有方案都收敛于:ConnectHelper#connectToNetwork(NetworkUpdateResult result,ActionListenerWrapper wrapper, int callingUid,String packageName)
三、wifi开关实现与现状
Android 10之前 :
权限:android.permission.CHANGE_WIFI_STATE、android.permission.ACCESS_WIFI_STATE
WifiManager wifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
boolean b = wifiManager.setWifiEnabled(true);
Android 10之后:该接口被限制,官方推荐方案(拉起设置面板来操作)
Intent panelIntent = new Intent(Settings.Panel.ACTION_WIFI);
startActivity(panelIntent);
限制代码分析:
总体来说就是:三方应用targetSdkVersion大于30就不能用了。
除了官方推荐的拉起系统面板来操作,还有没有更好的办法?
基于com.android.settings 利用activity弹dialog实现分析:
<activity android:theme="@android:style/Theme.DeviceDefault.Light.Dialog.Alert" android:name="com.android.settings.wifi.RequestToggleWiFiActivity" android:permission="android.permission.CHANGE_WIFI_STATE" android:exported="true" android:excludeFromRecents="true">
<intent-filter>
<action android:name="android.net.wifi.action.REQUEST_ENABLE"/>
<action android:name="android.net.wifi.action.REQUEST_DISABLE"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
com.android.settings.wifi.RequestToggleWiFiActivity
protected void onCreate(Bundle bundle) {
super.onCreate(bundle);
getWindow().addSystemFlags(524288);
this.mWiFiManager = (WifiManager) getSystemService(WifiManager.class);
setResult(0);
String stringExtra = getIntent().getStringExtra("android.intent.extra.PACKAGE_NAME"); // 需要传packageName
if (TextUtils.isEmpty(stringExtra)) {
finish();
return;
}
try {
this.mAppLabel = getPackageManager().getApplicationInfo(stringExtra, 0).loadSafeLabel(getPackageManager(), 1000.0f, 5);
String action = getIntent().getAction();
action.hashCode();
// 接收action,更新mState。
if (action.equals("android.net.wifi.action.REQUEST_ENABLE")) {
this.mState = 1;
} else if (action.equals("android.net.wifi.action.REQUEST_DISABLE")) {
this.mState = 3;
} else {
finish();
}
} catch (PackageManager.NameNotFoundException unused) {
Log.e("RequestToggleWiFiActivity", "Couldn't find app with package name " + stringExtra);
finish();
}
}
protected void onStart() {
...
updateUi(); //拉起Dialog,根据mState触发mWiFiManager.setWifiEnabled,
}
代码实现:
WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_PACKAGE_NAME, "com.android.settings"); // 必要条件
String action = wifiManager.isWifiEnabled() ? "android.net.wifi.action.REQUEST_DISABLE" : "android.net.wifi.action.REQUEST_ENABLE";
intent.setAction(action);
context.startActivity(intent);
效果: