Android - Android R 以上的热点开启

7,725 阅读5分钟

前言

在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)支持

开启流程

  1. 使用WifiManager#registerSoftApCallback注册回调以获取设备功能。 SoftApCallback回调提供了几种方法:

  2. 通过调用WifiManager#setSoftApConfiguration方法并提供SoftApConfiguration实例来配置要用于网络共享的软 AP 配置。您可以使用SoftApConfiguration.Builder类构造SoftApConfiguration

  3. 通过在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 ,从而允许控制应用程序(即设置应用程序)采取行动,例如例如,要求用户确认,然后根据用户的行为将设备添加到允许列表或阻止列表。

    请注意,设备只有在设备支持的情况下才能使用允许列表功能。您可以使用SoftApCapability#areFeaturesSupported(SOFTAP_FEATURE_CLIENT_FORCE_DISCONNECT)验证设备支持。

参考官方文档:网络共享的 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…

仍在完善中......