Android 热点
一、前言
热点开发属于系统级功能开发 ,涉及的核心 API 多为系统签名权限保护(如android.permission.TETHER_PRIVILEGED
),通常仅系统应用(如 Settings)可正常调用。
实际开发中,除基础的开关、配置功能外,可能需要扩展自定义信道设置、频段切换等 Settings 未涵盖的功能。本文总结热点开发的核心流程、调试技巧及版本适配要点
二、热点开发
1、开关和默认配置
(1)核心 API(分版本)
-
Android 13 及以上 :推荐使用
ConnectivityManager
与TetheringManager
的公开 API,支持回调监听状态:// 开启热点 ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); TetheringManager tetheringManager = connectivityManager.getTetheringManager(); tetheringManager.startTethering( TetheringManager.TETHERING_WIFI, ContextCompat.getMainExecutor(context), new TetheringManager.StartTetheringCallback() { @Override public void onTetheringStarted() { /* 开启成功 */ } @Override public void onTetheringFailed() { /* 开启失败 */ } } ); // 关闭热点 tetheringManager.stopTethering(TetheringManager.TETHERING_WIFI);
-
Android 11-12 :可使用
WifiManager
的startTetheredHotspot
(需系统签名):WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); // 开启(使用现有配置) wifiManager.startTetheredHotspot(null); // 关闭 wifiManager.stopTetheredHotspot();
(2)配置信息设置
通过SoftApConfiguration
配置热点参数(SSID、密码、频段、信道等),需注意配置修改后需重启热点才能生效:
private SoftApConfiguration buildConfig() {
SoftApConfiguration.Builder builder = new SoftApConfiguration.Builder();
builder.setSsid("MyHotspot"); // 热点名称
// 加密类型:0=无密码(OPEN),1=WPA2-PSK
builder.setPassphrase("12345678", SoftApConfiguration.SECURITY_TYPE_WPA2_PSK);
// 频段与信道(必须匹配,否则开启失败)
// 2.4G频段(band=1):信道1-14;5G频段(band=2):信道36-165
builder.setChannel(6, 1); // 2.4G信道6
return builder.build();
}
// 应用配置
WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
wifiManager.setSoftApConfiguration(buildConfig());
2、主要流程
热点开启的核心调用链
1. ConnectivityManager.startTethering() → 触发 tethering 服务请求
2. TetheringManager.startTethering() → 管理 tethering 状态与权限校验
3. TetheringService.startTethering() → 系统服务层处理请求
4. WifiManager.startTetheredHotspot() → 调用WiFi服务开启热点
5. WifiServiceImpl.startTetheredHotspot() → 实现热点启动逻辑
6. ActiveModeWarden.startSoftAp() → 管理活跃模式(AP模式)
7. SoftApManager.start() → 构建AP配置并调用底层
8. WifiNative.startSoftAp() → 与WiFi HAL交互
9. HostapdHal.addAccessPoint() → 调用硬件抽象层接口,启动hostapd进程
关键节点 :SoftApManager
负责 AP 配置转换,HostapdHal
对接硬件驱动,若某环节日志缺失,需重点排查对应层问题(如权限、硬件支持)。
3、相关日志
调试热点问题时,需重点关注以下日志关键字和过滤命令:
日志类型 | 关键字 / 进程名 | 过滤命令示例 | ||
---|---|---|---|---|
Framework 层 | SoftApManager 、WifiService |
`logcat | grep -iE "SoftApManager | WifiService"` |
底层服务 | hostapd 、wpa_supplicant |
`logcat | grep -i hostapd` | |
HAL 与驱动交互 | WifiNative 、WifiHAL |
`logcat | grep -i WifiNative` | |
网络接口 | wlan 、ap0 (接口名) |
`logcat | grep -i wlan1` |
常见错误日志分析:
Failed to start softap: invalid channel
:信道与频段不匹配(如 5G 频段用了 2.4G 信道)。SecurityException: Not allowed to start tethering
:缺少系统签名或TETHER_PRIVILEGED
权限。hostapd: Failed to set channel
:硬件不支持该信道(需检查设备支持的频段范围)。
4、相关广播
热点状态变化的核心广播为WifiManager.WIFI_AP_STATE_CHANGED_ACTION
,可监听开关过程:
private BroadcastReceiver hotspotReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (WifiManager.WIFI_AP_STATE_CHANGED_ACTION.equals(intent.getAction())) {
int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE, -1);
switch (state) {
case WifiManager.WIFI_AP_STATE_ENABLED:
Log.d(TAG, "热点已开启");
// 可在此处获取热点IP(通过NetworkInterface)
break;
case WifiManager.WIFI_AP_STATE_FAILED:
Log.e(TAG, "热点开启失败");
break;
// 其他状态:开启中(WIFI_AP_STATE_ENABLING)、关闭中(WIFI_AP_STATE_DISABLING)、已关闭(WIFI_AP_STATE_DISABLED)
}
}
}
};
// 注册广播
IntentFilter filter = new IntentFilter(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
context.registerReceiver(hotspotReceiver, filter);
三、其他
1、Android 热点开发调试小结
-
权限依赖 :必须为系统应用(安装在
/system/priv-app
)并使用系统签名,还需要导入framwork包; -
硬件限制 :频段和信道支持依赖 WiFi 芯片,需通过
WifiManager.getConfiguredNetworks()
或底层日志确认设备能力。// 依赖ZXing库:implementation 'com.google.zxing:core:3.5.0'
private Bitmap generateHotspotQrCode(String ssid, String password) {
// 二维码内容格式:WIFI:S:热点名;T:加密类型(WPA/WEP/OPEN);P:密码;;
String content = "WIFI:S:" + ssid + ";T:WPA;P:" + password + ";;";
MultiFormatWriter writer = new MultiFormatWriter();
try {
BitMatrix matrix = writer.encode(
content,
BarcodeFormat.QR_CODE,
300, 300, // 二维码宽高
new HashMap<>()
);
// 转换BitMatrix为Bitmap
int width = matrix.getWidth();
int height = matrix.getHeight();
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
bitmap.setPixel(x, y, matrix[x][y] ? Color.BLACK : Color.WHITE);
}
}
return bitmap;
} catch (WriterException e) {
e.printStackTrace();
return null;
}
}