Android获取连接到手机热点上的设备信息

时间:2024-04-03 15:07:51
主题:在手机开启热点网络的情况下,想要获取是哪个设备已经连接上了当前开启的热点。

实现思路:Android通过读取  /proc/net/arp 文件可以得到连接当前热点的设备信息,包括Mac地址、IP地址等信息。

一. 方法逻辑:

    /**
     * 获取连接到手机热点上的设备信息
     * @return
     */
    public List<HashMap> getConnectedApInfo() {
        List<HashMap> connectedApInfo = new ArrayList<>();

        try {
            BufferedReader br = new BufferedReader(new FileReader("/proc/net/arp"));
            String line;

            while ((line = br.readLine()) != null) {
                /**
                 * 获取到的数组结果,示例:[192.168.227.138, 0x1, 0x2, 82:64:5e:01:49:fc, *, wlan2]
                 */
                String[] splitted = line.split(" +");
                HashMap hashMap = new HashMap();

                //设备信息判断标准
                if (splitted.length >= 4 && splitted[3].contains(":")) {
                    String ip = splitted[0];          //获取IP地址信息,代替设备名称
                    String address = splitted[3];     //获取Mac地址信息

                    hashMap.put("name", ip);
                    hashMap.put("address", address);

                    connectedApInfo.add(hashMap);
                    Log.d(TAG, "getConnectedApInfo(),获取连接到手机热点上的设备信息:" + Arrays.toString(splitted) + "    connectedApInfo:" + connectedApInfo.size() + "  " + connectedApInfo);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return connectedApInfo;
    }
二. 拓展工具类,控制热点的开启和关闭,热点信息的获取:
import static android.content.Context.CONNECTIVITY_SERVICE;
import java.io.BufferedReader;
import java.io.FileReader;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.ConnectivityManager;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.util.Log;
import com.android.dx.stock.ProxyBuilder;

/**
 * Description:控制热点的开启和关闭,热点信息的获取
 * 所需权限:
 * <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
 * <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
 * <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
 * <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
 * <uses-permission android:name="android.permission.WRITE_SETTINGS"
 *     tools:ignore="ProtectedPermissions" /> <!-- 用于Android 6.0 (API 级别 23) 及以上版本 -->
 */
public class WifiHotspotManager {
    private static final String TAG = WifiHotspotManager.class.getSimpleName();
    private WifiManager wifiManager;
    private Method method;

    public WifiHotspotManager(Context context) {
        wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
    }

    public boolean isApEnabled() {
        try {
            if (method == null) {
                method = wifiManager.getClass().getMethod("isWifiApEnabled");
            }
            return (boolean) method.invoke(wifiManager);
        } catch (Exception e) {
            Log.e(TAG, "Error checking if AP is enabled", e);
            return false;
        }
    }

    /**
     * 开启或关闭热点
     * @param enabled
     * @return
     */
    public boolean setApEnabled(boolean enabled) {
        if (enabled) {
            Log.d(TAG, "开启热点,Enabling hotspot");
        } else {
            Log.d(TAG, "关闭热点,Disabling hotspot");
        }

        try {
            if (method == null) {
                method = wifiManager.getClass().getMethod("setWifiApEnabled", WifiConfiguration.class, boolean.class);
            }
            return (boolean) method.invoke(wifiManager, null, enabled);
        } catch (Exception e) {
            Log.e(TAG, "开启或关闭热点 is error,enabling/disabling AP:" + e);
            return false;
        }
    }

    /**
     * 配置热点的设置(如SSID和密码)
     * 需要创建一个WifiConfiguration对象来配置你的热点设置,然后将其传递给configureApState方法
     * @param apConfig
     * @return
     */
    public boolean configureApState(WifiConfiguration apConfig) {
        try {
            Method method = wifiManager.getClass().getMethod("setWifiApConfiguration", WifiConfiguration.class);
            return (boolean) method.invoke(wifiManager, apConfig);
        } catch (Exception e) {
            Log.e(TAG, "Error setting AP configuration", e);
            return false;
        }
    }

    /**
     * 控制热点的开启和关闭(一步到位)
     */
    public boolean controlApSwitch(Context context, boolean flag){
        WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
        try{
            WifiConfiguration apConfig = new WifiConfiguration();
            String deviceModel = Build.MODEL;     //设备型号
            apConfig.SSID = deviceModel;          //配置热点的名称
            apConfig.preSharedKey = "12345678";   //配置热点的密码(至少8位)
            apConfig.allowedKeyManagement.set(4); //配置密码加密方式

            //通过反射调用设置热点
            Method method = wifiManager.getClass().getMethod("setWifiApEnabled", WifiConfiguration.class, Boolean.TYPE);
            Boolean rs = (Boolean) method.invoke(wifiManager, apConfig, flag);        //true 开启热点 ,false 关闭热点
            Log.d(TAG, flag ? "当前设备:" + deviceModel + " 开启热点网络是否成功:" + rs : " 关闭热点网络是否成功:" + rs);
            return rs;
        } catch(Exception e){
            e.printStackTrace();
            return false;
        }
    }


    /**
     * 发送隐式广播。此方法会查询与给定意图相匹配的所有广播接收器,并向每个匹配的接收器发送一个显式广播。
     *
     * @param context 上下文,用于发送广播。
     * @param intent 需要发送的隐式广播意图。
     * @param action 执行的动作
     */
    public void sendImplicitBroadcast(Context context, Intent intent, String action) {
        // 获取包管理器,用于查询广播接收器
        PackageManager pm = context.getPackageManager();
        // 查询与intent相匹配的所有广播接收器
        List<ResolveInfo> matches = pm.queryBroadcastReceivers(intent, 0);

        // 遍历所有匹配的广播接收器
        for (ResolveInfo resolveInfo : matches) {
            // 创建一个新的意图,将其转换为显式意图,目标为当前接收器
            Intent explicit = new Intent(intent);
            // 设置组件名称,转换为显式意图
            ComponentName cn = new ComponentName(resolveInfo.activityInfo.applicationInfo.packageName, resolveInfo.activityInfo.name);
            explicit.setComponent(cn);
            // 向每个匹配的广播接收器发送显式广播
            context.sendBroadcast(explicit);
        }

        Log.d(TAG, "sendImplicitBroadcast(),发送隐式广播,action:" + action);
    }

    /**
     * 打开手机的热点
     * 需要在build.gradle文件添加三方库依赖:implementation 'com.linkedin.dexmaker:dexmaker-mockito:2.12.1'
     * @param context
     */
    public void startTethering(Context context){
        ConnectivityManager connectivityManager = ((ConnectivityManager)context.getSystemService(CONNECTIVITY_SERVICE));
        try{
            Class classOnStartTetheringCallback = Class.forName("android.net.ConnectivityManager$OnStartTetheringCallback");
            Method startTethering=connectivityManager.getClass().getDeclaredMethod("startTethering",int.class,boolean.class,classOnStartTetheringCallback);
            Object proxy = ProxyBuilder.forClass(classOnStartTetheringCallback).handler(new InvocationHandler(){
                @Override
                public Object invoke(Object o,Method method,Object[]objects)throws Throwable{
                    return null;
                }
            }).build();
            startTethering.invoke(connectivityManager,0,false,proxy);
        } catch(Exception e){
            e.printStackTrace();
        }

        Log.d(TAG, "startTethering(),打开手机的热点");
    }

    /**
     * 关闭手机的热点
     */
    public void stopTethering(Context context){
        ConnectivityManager connectivityManager=((ConnectivityManager)context.getSystemService(CONNECTIVITY_SERVICE));
        try{
            Method stopTethering = connectivityManager.getClass().getDeclaredMethod("stopTethering",int.class);
            stopTethering.invoke(connectivityManager,0);
        }catch(Exception e){
            e.printStackTrace();
        }
        Log.d(TAG, "stopTethering(),关闭手机的热点");
    }

    /**
     * 判断是否开启手机的热点
     * @param context
     * @return
     */
    public boolean isWifiApEnabled(Context context){
        WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(
                Context.WIFI_SERVICE);
        try{
            Method method=wifiManager.getClass().getMethod("isWifiApEnabled");
            method.setAccessible(true);
            return(Boolean)method.invoke(wifiManager);
        }catch(NoSuchMethodException e){
            e.printStackTrace();
        }catch(Exception e){
            e.printStackTrace();
        }

        Log.d(TAG, "isWifiApEnabled(),判断是否开启手机的热点");
        return false;
    }

    /**
     * 获取手机的热点信息
     * @param context
     * @return
     */
    public String getApInfo(Context context){
        WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
        try{
            Method localMethod = wifiManager.getClass().getDeclaredMethod("getWifiApConfiguration", new Class[0]);
            Object config = localMethod.invoke(wifiManager);
            Log.d(TAG, "getApInfo(),获取手机的热点信息:" + config.toString());
        }catch(Exception localException){
            localException.printStackTrace();
        }
        return null;
    }

    /**
     * 获取连接到手机热点上的设备信息
     * @return
     */
    public List<HashMap> getConnectedApInfo() {
        List<HashMap> connectedApInfo = new ArrayList<>();

        try {
            BufferedReader br = new BufferedReader(new FileReader("/proc/net/arp"));
            String line;

            while ((line = br.readLine()) != null) {
                /**
                 * 获取到的数组结果,示例:[192.168.227.138, 0x1, 0x2, 82:64:5e:01:49:fc, *, wlan2]
                 */
                String[] splitted = line.split(" +");
                HashMap hashMap = new HashMap();

                //设备信息判断标准
                if (splitted.length >= 4 && splitted[3].contains(":")) {
                    String ip = splitted[0];          //获取IP地址信息,代替设备名称
                    String address = splitted[3];     //获取Mac地址信息

                    hashMap.put("name", ip);
                    hashMap.put("address", address);

                    connectedApInfo.add(hashMap);
                    Log.d(TAG, "getConnectedApInfo(),获取连接到手机热点上的设备信息:" + Arrays.toString(splitted) + "    connectedApInfo:" + connectedApInfo.size() + "  " + connectedApInfo);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return connectedApInfo;
    }
三. 调用示例:
//返回的是一个数据形式是包含HahMap集合的List集合,可根据HashMap的键值对取值并显示
WifiHotspotManager wifiHotspotManager = new WifiHotspotManager(requireActivity());

List<HashMap> connectedApInfo = wifiHotspotManager.getConnectedApInfo();

Log.d(TAG, "connectedApInfo:" + connectedApInfo.size() + "  " + connectedApInfo);
四.手机热点已连接设备与功能效果图

参考文章:Android获取实时连接热点的设备IP_安卓设备获取ip-CSDN博客