接下来要更新的博文是WiFi热点相关的,更新时间为8月1号–8月30号之间。看到此博文的开发者们,如果有关于WiFi热点的任何疑问可留言,最终会将值得研究的问题以及我已经研究出来的问题更新在博文上。
Android虐我千百遍,我待Android如初恋。
转载注明出处 本文出自fanfan的WiFi热点探究实录
——————编辑于2017-08-02———————
wifi热点说的是wifiAp相关,所以如果源码开发的话,这个WifiAp算是一个搜索代码的关键字,含义是Wifi Access point,wifi接入点。所以下文中的wifi热点统一用WifiAp代替
- wifiAp打开方式:设置->更多->移动网络共享->便携式wlan热点。
- wifiAp打开条件:任何情况下均可。只是有内网外网之分。造成内外网之分的影响条件有sim卡和wifi的连接状态。注意,这里所说的是wifi的连接状态,而不是wifi热点的连接状态
- wifiAp开发中用处:可用于局域网内的通信
- wifiAp开发中相关问题:
- 第一,跟WiFiAp相关的有wifiAp的网关Ip,以及ip范围
- 第二,wifiAp的config:包括初始创建时的defaultvalue:名字(ssid)和密码(preSharedKey),以及后续修改config
- 第三,wifiAp的enable状态
- 第四,wifiAp的设备连接列表:一是保证能获取到当前连接设备列表,二是当有设备连接时能够实时的更新
- 第五,wifiAp的连接限制:包括最大连接数限制,以及黑白名单机制
先就wifiAp的ip进行说明:
既然是要局域网内通信,那就要用到ip地址和端口号了(关于端口号的设定属于开发通信时的问题,是用户自定义的可变的,在我的程序里我规定端口号为80。而ip地址是有规定的,所以只讲关于ip的问题)。ip地址是在Android源码中规定好的,平常所买的路由器的ip地址一般都是192.168.0.1。Android源码中所规定的手机的wifiAp的ip地址为192.168.43.1,这个代码中可以看到
- 创建wifiAp时的ip:在创建wifiAp时相当于网关ip,/frameworks/opt/net/wifi/service/java/com/android/server/wifi/SoftApManager.java中开启wifiAp时规定了ip地址(Android7.0中在该文件中,如果是其他Android系统可以在WifiStateMachine),具体方法为startThering中
- wifiAp的ip地址的分配区间:在/frameworks/base/services/core/java/com/android/server/connectivity/Tethering.java中有规定
//usb网络共享的网关是192.168.42.1
// USB is 192.168.42.1 and 255.255.255.0
//wifi便携式热点网关是192.168.43.1
// Wifi is 192.168.43.1 and 255.255.255.0
//蓝牙共享(个人局域网)限制5个
// BT is limited to max default of 5 connections. 192.168.44.1 to 192.168.48.1
// with 255.255.255.0
private String[] mDhcpRange;
private static final int TETHER_RETRY_UPSTREAM_LIMIT = 5;
private static final String[] DHCP_DEFAULT_RANGE = {
"192.168.42.2", "192.168.42.254", "192.168.43.2", "192.168.43.254",
"192.168.44.2", "192.168.44.254", "192.168.45.2", "192.168.45.254",
"192.168.46.2", "192.168.46.254", "192.168.47.2", "192.168.47.254", "192.168.48.2", "192.168.48.254",
}
——————编辑于2017-08-03———————
WifiAp的config分析:
默认的config:
代码位置
在恢复出厂设置后打开WifiAp,初始的wifiAp的名称是一定的,但是wifiAp的密码是随机,这个可以自行测试,实现代码位于一个叫做 WifiApConfigStore.java的文件中,文件路径为/frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiApConfigStore.java代码实现
/** * 构建一个默认的wifiAp,加密类型是WPA2,密码随机 * * We are changing the Wifi Ap configuration storage from secure settings to a * flat file accessible only by the system. A WPA2 based default configuration * will keep the device secure after the update. */ private WifiConfiguration getDefaultApConfiguration() { WifiConfiguration config = new WifiConfiguration(); //wifiAp的ssid config.SSID = mContext.getResources().getString( R.string.wifi_tether_configure_ssid_default); //wifiAp的加密方式 config.allowedKeyManagement.set(KeyMgmt.WPA2_PSK); //随机生成uuid String randomUUID = UUID.randomUUID().toString(); //first 12 chars from xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx config.preSharedKey = randomUUID.substring(0, 8) + randomUUID.substring(9, 13); return config; } }
修改wifiAp的config配置
如果想要修改wifiAp的config配置需要注意,在修改config时,config会直接设置下去,但是并不会立即生效,必须要重启wifiAp之后才有效。这个可以先拿自己的手机演示确认。
- 首先获取到wifiManager对象
WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
- 然后获取到config对象
WifiConfiguration config = wifiManager.getWifiApConfiguration();
- 有了config之后,就可以对参数进行设置了,比如设置用户名和密码
if (config != null) { config.SSID = mWifiInfoBean.getApSsid(); config.preSharedKey = mWifiInfoBean.getPskKey(); }
当然,你还可以做其他设置,具体的可以参考WifiConfiguration.java源码 到这一步,对于wifiAp的用户名和密码已经设置成功了,此时若手动重启wifiAp后config即可生效。如果你想要立刻生效,那就必须要重启wifiAp了。- 重启wifiAp,将所设置的config设置进去,并重启热点,流程是首先判断WiFi热点是否处于开启状态,如果是,则重启wifiAp。如果当前wifiAp不处于开启状态,则只需要把config设置下去即可
if (wifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_ENABLED) { //如果wifiAp处于开启状态,则关闭并重启 wifiManager.setWifiApEnabled(null, false); return wifiManager.setWifiApEnabled(config, true); } else { //如果wifiAp不处于开启状态,则只需要将config设置下去 return wifiManager.setWifiApConfiguration(config); }
——————编辑于2017-08-04———————
开启/关闭WifiAp热点状态
在对wifiAp进行config修改时已经涉及到了对于wifiAp的开和关,在进行wifiAp进行开关的过程中需要传入config,如果传入的为null,则沿用上一次的 config,如果上一次的config不存在,则会去加载默认的config。当开启wifiAp时会先去判断wifi的状态,如果wifi处于开启状态则需要关闭WiFi状态,然后开启wifiAp。
- 获取WiFimanager对象(参考上文)
- 判断目前wififAp的开关状态,如果处于开启状态,则不进行任何操作。当然,如果你想自己设置config,那么就照着上文中配置config的步骤来
/** * if wifiap is enabled */ if (wifiManager.isWifiApEnabled()) { return true; }
- 判断wifi的状态,如果处于开启状态,则关闭wifi状态
/** * Disable Wifi if enabling tethering */ int wifiState = wifiManager.getWifiState(); if (enable && ((wifiState == WifiManager.WIFI_STATE_ENABLING) || (wifiState == WifiManager.WIFI_STATE_ENABLED))) { wifiManager.setWifiEnabled(false); }
- 接下来就可以调用wifiAp开启的方法了
wifiManager.setWifiApEnabled(null, enable);
已连接设备列表
读取wifiAp的已连接设备列表
这个很纠结,关于wifiAp的这些东西不存在什么jni接口,只能是通过读文件或者是监听广播来和底层通信。Android源码中提供了一个读取已连接设别列表的方法——读取特定文件“/proc/net/arp” 来获取已连接设备信息。
代码如下:
File file = new File("/proc/net/arp");
try {
reader = new BufferedReader(new FileReader(file));
String line;
while ((line = reader.readLine()) != null) {
String[] tokens = line.split("[ ]+");
if (tokens.length < 6 || tokens[3].length() < 8) {
continue;
}
//角标为3是mac地址,角标为0是ip地址 ,设备名是根据mac来获取
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
该文件包含的数据有sscanf(buf, “%s 0x%x 0x%x %s %s %s\n”, ip, &h_type, &flag, hw_addr, mask, dev )
也就是说tokens 长度为6,可以看到包含已连接设备的ip和addr,但是设备名却没有说明,这个需要自己根据mac地址来获取对应的厂商和设备名。当然,方案提供商也许自己会集成这部分工作,所以具体情况具体考虑