先要 弄清楚以下几点:
一、 Android显示的WiFi名字,计算机都是以二进制处理的数据的,所以接受到的这个名字一定是一个二进制数据,它是怎么变成字符串的呢?
在frameworks/base/wifi/java/android/net/wifi/WifiSsid.java中
@Override
public String toString() {
byte[] ssidBytes = octets.toByteArray();
// Supplicant returns \x00\x00\x00\x00\x00\x00\x00\x00 hex string
// for a hidden access point. Make sure we maintain the previous
// behavior of returning empty string for this case.
if (octets.size() <= 0 || isArrayAllZeroes(ssidBytes)) return "";
// TODO: Handle conversion to other charsets upon failure
Charset charset = Charset.forName("UTF-8");
CharsetDecoder decoder = charset.newDecoder()
.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE);
CharBuffer out = CharBuffer.allocate(32);
CoderResult result = decoder.decode(ByteBuffer.wrap(ssidBytes), out, true);
out.flip();
if (result.isError()) {
return NONE;
}
return out.toString();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
这段代码很明显是用了utf-8规则解码了ssidBytes,ssidBytes在这里是一个字节数组,也就是Android所收到的WiFi名称。问题来了,有些路由器的WiFi名称是GBK编码的,这里用utf-8编码很显然得不到正确的结果。所以看到的是乱码,这里就有要兼容GBK编码的问题。如果这里修改为兼容GBK编码是不是就可以了,修改后会发现,显示正常了但是连不上,为什么呢,那就要知道连接过程了。
二、WiFi正真的连接工作是wpa_supplicant完成的。一般在init.xxxx.rc中会看到一个service 如下
service wpa_supplicant /system/bin/wpa_supplicant -iwlan0 -Dnl80211 -c/data/misc/wifi/wpa_supplicant.conf -O/data/misc/wifi/sockets -e/data/misc/wifi/entropy.bin -g@android:wpa_wlan0
class main
user root
group system wifi
socket wpa_wlan0 dgram 660 wifi wifi
disabled
oneshot
这里有一个配置文件wpa_supplicant.conf,Android会把WiFi热点名称,密码等写入这个文件,wpa_supplicant会通过这个配置文件去获取WiFi名称(ssid)和密码, 然后与扫描到的进行比较,然后找到匹配的热点然后再连接,比较函数位于external/wpa_supplicant_8/wpa_supplicant/events.c
static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
int i, struct wpa_bss *bss,
struct wpa_ssid *group,
int only_first_ssid)
{
u8 wpa_ie_len, rsn_ie_len;
int wpa;
struct wpa_blacklist *e;
const u8 *ie;
struct wpa_ssid *ssid;
int osen;
//省略部分代码
if (check_ssid &&
(bss->ssid_len != ssid->ssid_len ||
os_memcmp(bss->ssid, ssid->ssid, bss->ssid_len) != 0)) {
wpa_dbg(wpa_s, MSG_DEBUG, " skip - SSID mismatch");
continue;
}
if (ssid->bssid_set &&
os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0) {
wpa_dbg(wpa_s, MSG_DEBUG, " skip - BSSID mismatch");
continue;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
在上面可以看到比较了ssid,问题就在这里这里比较是通不过的,因为配置文件中的ssid是utf-8编码的,而扫描到的是GBK编码,而且这里你也不能强制让他通过,因为在wpa_supplicant不仅仅是这里用到了配置文件中的ssid。那怎么办,你肯定会想到是谁用utf-8把ssid保存到配置文件里的。
三、谁保存的这个ssid和密码
在frameworks/opt/net/wifi/service/java/com/android/server/wifi/WifiConfigStore.java中
private String encodeSSID(String str){
String tmp = removeDoubleQuotes(str);
return String.format("%x", new BigInteger(1, tmp.getBytes(Charset.forName("UTF-8"))));
}
这里会把String 也就是你看到的WiFi名称,转换成二进制数据,这里的二进制数据会存入wpa_supplicant.conf,这就是为什么比较通不过的原因了。
解决方法
了解了这个多,现在应该有一个思路了,显示名称时需要修改WifiSsid.java中的toString,以兼容GBK,存储wpa_supplicant.conf,又需要正确的转换成二进制数。
我的解决方法是在WifiSsid.java中的toString方法中兼容GBK,并且用map存储string和二进制数的键值对。然后WifiConfigStore.java中的encodeSSID方法用string去这个map中取出二进制数,注意有一种情况map中没有这个string,map中存储了所有扫描显示了的WiFi热点,还有一种map中没有就是隐藏了的热点,如果如果map中没有,还需要用utf-8或者是GBK转化成二进制。
|