前言
在Android R (11) 版本以后,网络共享的 Wi-Fi 热点开启接口由 android.net.ConnectivityManager 变更为 android.net.TetheringManager. 原来旧的 ConnectivityManager 被弃用了,这里通过 TetheringManager 进行热点的开启。
Wi-Fi 热点的配置信息
从 Android 11 开始,Wi-Fi 热点(软AP)网络共享的支持的自定内容包括以下配置:
-
SSID 和 BSSID
-
安全类型(包括WPA3)
-
隐藏的SSID
-
工作频段和频道(包括ACS)
-
允许的最大客户端数
-
自动关机超时值
-
允许用户控制关联设备的允许列表和阻止列表
注:部分 API 为系统 API,只有系统级别的应用方能访问。
参考官方文档:网络共享的 Wi-Fi 热点(软 AP)支持
开启流程
-
使用
WifiManager#registerSoftApCallback注册回调以获取设备功能。SoftApCallback回调提供了几种方法:SoftApCallback#onCapabilityChanged:提供有关设备功能的信息,包括支持的客户端的最大数量,以及是否支持 SAE 或 ACS。SoftApCallback#onInfoChanged:提供有关正在运行的 Soft AP 的信息(仅在启动后有效),包括频段和频率信息。SoftApCallback#onConnectedClientsChanged:提供已连接客户端的列表。对于每个客户端,您可以获得 MAC 地址。要获取 IP 信息,请使用TetheringEventCallback#onClientsChanged回调。SoftApCallback#onStateChanged:在启用和禁用软 AP 时提供有关其状态的更新。SoftApCallback#onBlockedClientConnecting:提供被阻止的客户端信息以及以下阻止原因之一:设备达到了它可以支持的最大客户端数量,或者客户端没有被明确授权连接。
-
通过调用
WifiManager#setSoftApConfiguration方法并提供SoftApConfiguration实例来配置要用于网络共享的软 AP 配置。您可以使用SoftApConfiguration.Builder类构造SoftApConfiguration。 -
通过在
TetheringManager#startTethering调用网络共享方法开始网络共享。
参考官方文档:网络共享的 Wi-Fi 热点(软 AP)支持
实施允许和阻止列表
对允许关联到软 AP 的设备的控制:
-
使用
SoftApConfiguration.Builder#setMaxNumberOfClients限制可以关联到软 AP 的最大设备数量。确保指定的数字低于设备支持的最大客户端数。您可以从SoftApCapability#getMaxSupportedClients获得最大数量。 -
使用允许和阻止列表提供动态控制:
- 软 AP 的默认配置允许所有设备关联到软 AP,但 MAC 地址添加到
SoftApConfiguration.Builder#setBlockedClientList的设备除外。 - 如果使用
SoftApConfiguration.Builder#setClientControlByUserEnabled(true)配置软 AP,则使用允许列表。- MAC 地址在
SoftApConfiguration.Builder#setBlockedClientList中的所有设备都被阻止关联。 - MAC 地址在
SoftApConfiguration.Builder#setAllowedClientList中的所有设备都允许关联。 - 所有其他设备(即 MAC 地址不在允许或阻止列表中的设备)都将被阻止关联,但会调用
SoftApCallback#onBlockedClientConnecting,从而允许控制应用程序(即设置应用程序)采取行动,例如例如,要求用户确认,然后根据用户的行为将设备添加到允许列表或阻止列表。
- MAC 地址在
请注意,设备只有在设备支持的情况下才能使用允许列表功能。您可以使用
SoftApCapability#areFeaturesSupported(SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT)验证设备支持。 - 软 AP 的默认配置允许所有设备关联到软 AP,但 MAC 地址添加到
参考官方文档:网络共享的 Wi-Fi 热点(软 AP)支持
实现
开启热点步骤:
由于开启热点是属于系统隐藏的API,因此要实现开启热点一下条件:应用是系统应用,通过反射进行API的调用。
1、Manifest.xml 配置权限
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
<uses-permission android:name="android.permission.TETHER_PRIVILEGED"/>
1、在实现前,可以通过下面取巧的避免不必要的额外反射处理,在最外层创建 android.net 包名,并在其下创建类: TetheringManager ,类内容从源码中提取相应的内容,如下
package android.net;
import androidx.annotation.NonNull;
import java.util.concurrent.Executor;
/**
* Created by Momin.
*
* @ProjectName:
* @Package: android.net
* @ClassName: TetheringManager
* @Description: java类作用描述
* @Author:
* @CreateDate:
* @UpdateUser: 更新者
* @UpdateDate: 更新时间
* @UpdateRemark: 更新说明
* @Version: 1.0
*/
public class TetheringManager {
public @interface StartTetheringError {
}
/**
* Callback for use with {@link #startTethering} to find out whether tethering succeeded.
*/
public interface StartTetheringCallback {
/**
* Called when tethering has been successfully started.
*/
default void onTetheringStarted() {}
/**
* Called when starting tethering failed.
*
* @param error The error that caused the failure.
*/
default void onTetheringFailed(@StartTetheringError final int error) {}
}
/**
* Starts tethering and runs tether provisioning for the given type if needed. If provisioning
* fails, stopTethering will be called automatically.
*
* <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will
* fail if a tethering entitlement check is required.
*
* @param type The tethering type, on of the {@code TetheringManager#TETHERING_*} constants.
* @param executor {@link Executor} to specify the thread upon which the callback of
* TetheringRequest will be invoked.
* @hide
*/
public void startTethering(int type, @NonNull final Executor executor,
@NonNull final StartTetheringCallback callback) {
}
}
2、热点相关配置
热点配置和 Android 11 之前的配置差不多,网上有很多相关的博客可参考,后期看情况补充配置相关内容。
3、热点开启
private Context context;
public void startTethering(int type) {
Executor executor = new Executor() {
@Override
public void execute(Runnable command) {
}
};
TetheringManager.StartTetheringCallback callback = new TetheringManager.StartTetheringCallback() {
@Override
public void onTetheringStarted() {
}
};
try {
// 获取 TetheringManager 实例
Object obj = context.getSystemService("tethering");
// 反射获取类
Class clz = Class.forName("android.net.TetheringManager");
// 参数类型
Class<?>[] types = new Class[]{int.class, Executor.class, TetheringManager.StartTetheringCallback.class};
// 反射获取方法
Method objectMethod = obj.getClass().getMethod("startTethering", types);
// 调用 API
objectMethod.invoke(obj, type, executor, callback);
} catch (Exception e) {
e.printStackTrace();
}
}
只看开启的代码,可能很多参数不知道有什么用,参考源码:
/**
* Starts tethering and runs tether provisioning for the given type if needed. If provisioning
* fails, stopTethering will be called automatically.
*
* <p>Without {@link android.Manifest.permission.TETHER_PRIVILEGED} permission, the call will
* fail if a tethering entitlement check is required.
*
* @param type The tethering type, on of the {@code TetheringManager#TETHERING_*} constants.
* @param executor {@link Executor} to specify the thread upon which the callback of
* TetheringRequest will be invoked.
* @hide
*/
@RequiresPermission(anyOf = {
android.Manifest.permission.TETHER_PRIVILEGED,
android.Manifest.permission.WRITE_SETTINGS
})
@SystemApi(client = MODULE_LIBRARIES)
public void startTethering(int type, @NonNull final Executor executor,
@NonNull final StartTetheringCallback callback) {
startTethering(new TetheringRequest.Builder(type).build(), executor, callback);
}
方法注释说明的意思,我的蹩脚翻译:根据需要进行配置的绑定,开始绑定并运行给定的类型。如果配置失败,自动调用 stopTethering (结束热点开启)。
参数注释 type : 热点类型, 在 TetheringManager 中有相关类型,有下面8种,意思看注释
/**
* Invalid tethering type.
* @see #startTethering.
*/
public static final int TETHERING_INVALID = -1;
/**
* Wifi tethering type.
* @see #startTethering.
*/
public static final int TETHERING_WIFI = 0;
/**
* USB tethering type.
* @see #startTethering.
*/
public static final int TETHERING_USB = 1;
/**
* Bluetooth tethering type.
* @see #startTethering.
*/
public static final int TETHERING_BLUETOOTH = 2;
/**
* Wifi P2p tethering type.
* Wifi P2p tethering is set through events automatically, and don't
* need to start from #startTethering.
*/
public static final int TETHERING_WIFI_P2P = 3;
/**
* Ncm local tethering type.
* @see #startTethering(TetheringRequest, Executor, StartTetheringCallback)
*/
public static final int TETHERING_NCM = 4;
/**
* Ethernet tethering type.
* @see #startTethering(TetheringRequest, Executor, StartTetheringCallback)
*/
public static final int TETHERING_ETHERNET = 5;
/**
* WIGIG tethering type. Use a separate type to prevent
* conflicts with TETHERING_WIFI
* This type is only used internally by the tethering module
* @hide
*/
public static final int TETHERING_WIGIG = 6;
参数注释 executor : 调用 TetheringRequest 回调的线程。
参数 callback : 看 创建的 TetheringManager 中明白了当。
PS:
源码传送门: cs.android.com/android/pla…
文档传送门:source.android.com/devices/tec…
仍在完善中......