本文将分享赛门铁克首次涉足硬件领域:推出安全WiFi路由器的详细内容,并且还将对赛门铁克官网下载进行详尽解释,此外,我们还将为大家带来关于AndroidEmulator不适用于wifi路由器,但可以在
本文将分享赛门铁克首次涉足硬件领域:推出安全 WiFi 路由器的详细内容,并且还将对赛门铁克官网下载进行详尽解释,此外,我们还将为大家带来关于Android Emulator 不适用于 wifi 路由器,但可以在 macbook pro 中与移动热点配合使用、Android 中的 WiFi 学习笔记 (转载)----WIFI 启动 代码流程走读 --- 网络连接流程、Android 打开 WIFI 并快速获取 WIFI 的信息、Android 编程实现连接 Wifi (运用 Wifi 相关 API)的相关知识,希望对你有所帮助。
本文目录一览:- 赛门铁克首次涉足硬件领域:推出安全 WiFi 路由器(赛门铁克官网下载)
- Android Emulator 不适用于 wifi 路由器,但可以在 macbook pro 中与移动热点配合使用
- Android 中的 WiFi 学习笔记 (转载)----WIFI 启动 代码流程走读 --- 网络连接流程
- Android 打开 WIFI 并快速获取 WIFI 的信息
- Android 编程实现连接 Wifi (运用 Wifi 相关 API)
赛门铁克首次涉足硬件领域:推出安全 WiFi 路由器(赛门铁克官网下载)
安全软件巨头赛门铁克近日宣布推出首款消费级安全硬件——Norton Core Wi-Fi路由器,零售价格为280美元,甚至超过了Tp-link AD7200这样的发烧级4K无线路由器(2017年最好的家庭无线路由器TOP5),市场前景凶多吉少。
信息安全企业推出消费级家庭安全路由器并非新鲜事,例如中国的奇虎360和美国的Sophos都曾推出主打安全概念的家庭无线路由器或者安全网关产品,但市场成绩并未达到预期,作为安全企业,在WiFi路由器方面的产品力和技术、市场能力依然是最大的短板。赛门铁克此番杀入家庭无线路由器市场,从定价可以看出特意回避了竞争激烈的中低端产品,主打安全+时尚牌,定位“高端智能家居市场”。
智能家居的入口之争
此番赛门铁克试水消费级安全硬件,无疑也是觊觎潜力巨大的智能家居安全市场,据Markets and Markets最新报告,未来六年智能家居市场年复合增长率高达13%,到2023年市场规模将高达1389亿美元。而不起眼的WiFi路由器,则正是智能家居网络的枢纽和“入口”。
随着智能硬件和物联网设备数量的不断增长,智能家居面临的安全风险不断累计,而安全性和可管理性(例如父母对子女网络行为的管控,以及智能家庭硬件和应用之间的带宽资源优先级管理),也正在成为WiFi无线路由器最大的卖点之一。
WiFi路由器纷纷走上颜控路线
赛门铁克Norton Core安全无线路由器的最大亮点居然不是安全,而是时尚的外观设计——一个如钻石切割的多面体。这并非赛门铁克的心血来潮,或者想为市场注入一股清流,而是WiFi路由器市场的趋势使然。除了已经退出WiFi路由器竞争的苹果公司的产品外,去年Netgear推出了堪称惊艳的Orbi无线路由器,此外包括2015年Google与TP-Link合作推出的OnHub无线路由器、以及Google去年推出的售价129美元的WiFi模块路由器,无不走的是匹配现代化智能家居风格的“颜控”时尚路线。
文章来自IT经理网
Android Emulator 不适用于 wifi 路由器,但可以在 macbook pro 中与移动热点配合使用
如何解决Android Emulator 不适用于 wifi 路由器,但可以在 macbook pro 中与移动热点配合使用?
如果我将 Mac 与办公室 VPN 连接,我的 Android 模拟器无法与 wifi 路由器一起使用,但如果我将系统连接到移动热点,一切正常。
- Wifi 路由器 + 办公室 VPN + Android 模拟器 = 不工作
- Wifi 路由器 + Android 模拟器 = 工作正常
- 移动热点 + Office VPN + Android 模拟器 = 工作正常
解决方法
暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!
如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。
小编邮箱:dio#foxmail.com (将#修改为@)
Android 中的 WiFi 学习笔记 (转载)----WIFI 启动 代码流程走读 --- 网络连接流程
Android 的 WiFi
我们通常看到 WiFi 的守护进程 wpa_supplicant 在我们的 ps 的进程列表中,这个就是我们的 wifi 守护进程。wpa_supplicant 在 external/wpa_supplicant 里实现
wpa_supplicant 适配层是通用的 wpa_supplicant 的封装,在 Android 中作为 WIFI 部分的硬件抽象层来使用。wpa_supplicant 适配层主要用于封装与 wpa_supplicant 守护进程的通信,以提供给 Android 框架使用。它实现了加载,控制和消息监控等功能。
wpa_supplicant 适配层的头文件如下所示:
hardware/libhardware_legacy/include/hardware_legacy/wifi.h
我们看它的加载过程
Init 会在系统启动首先加载 init.rc 这个文件会加载所有 service,这是 linux 启动的第一个用户空间的应用(属于 linux 进程,不属于 Android 应用)。
Service wpa_supplicant /system/bin/wpa_supplicant –Dwext –iwlan0 –d –c /data/misc/wifi/wpa_supplicant.conf
#user wifi
#group wifi system
Socket wpa_eth0 dgram 0660 wifi system
Disabled
Oneshot
Serive dhcpcd /system/bin/dhcpcd –f /system/etc/dhcpcd/dhcpcd.conf –d eth0
Disabled
Onshot
On property:init.svc.wpa_supplicant=stopped
Stop dhcpcd
添加 /system/etc/wifi/wpa_supplicant.conf
Update_config=1
Ctrl_interface=/data/system/wpa_supplicant// 和 IFACE_DIR 对应
Eapol_verison=1
Ap_scan=1
Fast_reauth=1
通过 linux 内核模块 /system/lib/modules/wlan.ko 这个 wifi 模块定义在 /hardware/libhardware_legacy/wifi/wifi.c
当 SystemServer 启动后会加载一系列的 Service 其中 init2 启动的就有 ConnectivityService。ConnectivityService.java (frameworks/base/services/java/com/android/server) 会管理所有的 Connectivity 相关的比如 APN,WiFi。看看是怎么启动 WiFi Service 的:
if (DBG) Log.v(TAG, "Starting Wifi Service.");
WifiStateTracker wst = new WifiStateTracker(context, mHandler);
WifiService wifiService = new WifiService(context, wst);
ServiceManager.addService(Context.WIFI_SERVICE, wifiService);
WifiStateTracker 会创建 WifMonitor 来接受来自底层的事件。WifiService 和 WifiMonitor 是整个模块的核心部分,WifiService 负责启动关闭 wpa_supplicant、发命令给 wpa_supplicant 进程,WiFiMonitor 负责从 wpa_supplicant 接收事件
整个流程是
SystemServer -> ServerThread -> ConnectivityService -> ConnectivityThread -> WifiTracker->WifiService -> WifiMonitor
WiFi 的启动过程
用户在设置界面下开启了 WiFi,调用应用程序 Settings 中的 setWifiEnabler 的 onPerferenceChange, 再由 WifiEnable 调用 WifiService,发送 MESSAGE_ENABLE_WIFI, 首先装载 wifi 内核模块 wlan.ko 然后启动 wpa_supplicant(用 /data/misc/wifi/wpa_supplicant.conf 配置),再通过 WifiStateTracker 来启动 WifiMonitor 监视线程
WifiSettings.java (packages/apps/settings/src/com/android/settings/wifi) 启动
mWifiEnabled = (CheckBoxPreference) preferenceScreen.findPreference(KEY_WIFI_ENABLED);
mWifiEnabler = new WifiEnabler(this, (WifiManager) getSystemService(WIFI_SERVICE),
mWifiEnabled);
这样就启动 WifiEnabler
WifiEnabler.java (packages/apps/settings/src/com/android/settings/wifi) 通过 WifiManager 调用 WifiManager.java (frameworks/base/wifi/java/android/net/wifi) setWifiEnabled 中的 IWifiManager 来启动 wifiservice [mService.setWifiEnabled (enabled);]
WifiService.java (frameworks/base/services/java/com/android/server) 又 setWifiEnabled () 这个里面的 sendEnableMessage (enable, true, Binder.getCallingUid ()); 来发送一则消息
Message msg = Message.obtain(mWifiHandler,
(enable ? MESSAGE_ENABLE_WIFI : MESSAGE_DISABLE_WIFI),
(persist ? 1 : 0), uid);
msg.sendToTarget (); 发送给自身的消息。
通过 WifiHandler 的 handleMessage 来维护这些消息,enable 的时候会调用 setWifiEnabledBlocking 这个函数,这个函数会做 setWifiEnabledState 然后做两件事: 1. 调用 wifi 本地方法 JNI 的 WifiNative.loadDriver
下面说本地方法 WifiNative.loadDriver 函数 WifiNative.java (frameworks/base/wifi/java/android/net/wifi) Android 的 WIFI 系统的 JNI 的部分:
frameworks/base/core/jni/android_net_wifi_Wifi.cpp 中的 android_net_wifi_loadDriver () 可以把 wifi 驱动模块装载
Wifi.c (hardware/libhardware_legacy/wifi) 内核模块 /system/lib/modules/wlan.ko 中的 wifi_load_driver ()
设置 wlan.driver.status 属性为 ok,至此 wifi 模块加载完毕。
2. 再来看看启动,同样是在 WifiService 中的 setWifiEnabledBlocking 这个函数会调用 startSupplicant 通过 WifiNative.java (frameworks/base/wifi/java/android/net/wifi) 的 startSupplicant 来启动 JNI:frameworks/base/core/jni/android_net_wifi_Wifi.cpp 的 android_net_wifi_startSupplicant 调用驱动模块 Wifi.c (hardware/libhardware_legacy/wifi) wlan.ko 中的 wifi_start_supplicant, Wifi 启动完毕
成功启动 wifi 之后 setWifiEnabledBlocking 运行 mWifiStateTracker.startEventLoop (); 事件循环,来监视事件 mWifiMonitor.startMonitoring (); à MonitorThread ().start (); 一直在线程里循环调用 WifiNative.waitForEvent (); 最后调用
setWifiEnabledState (eventualWifiState, uid); intent = new Intent (WifiManager.WIFI_STATE_CHANGED_ACTION); 广播消息向外界通知 wifi 已经成功启动了。
查找热点 AP
上面说了 WifiManager 发送广播 WIFI_STATE_CHANGED_ACTION,只要 Android 应用注册了接受该 Action 的就接受,我们的 WifiLayer 注册了接收到该 Action
WifiSettings.java (packages/apps/settings/src/com/android/settings/wifi) 中有 mWifiLayer.onCreate ();(这个函数创建 WifiLayer 指定接受的 Action)
WifiLayer.java (packages/apps/settings/src/com/android/settings/wifi) 中的 BroadcastReceiver 有一句话 else if (action.equals (WifiManager.WIFI_STATE_CHANGED_ACTION)) {
handleWifiStateChanged(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
WifiManager.WIFI_STATE_UNKNOWN));
这个函数会调用 loadConfiguredAccessPoints 和 attemptScan 来开始扫描,调用 WifiManager 的 mWifiManager.startScanActive,WifiManager.java 中的 mService.startScan 通过 WifiService 中的 startScan 通过本地方法 WifiNative.setScanResultHandlingCommand 启动 JNI android_net_wifi_Wifi.cpp (frameworks/base/core/jni) 中的 android_net_wifi_setScanResultHandlingCommand 的命令 “AP_SCAN 模式” Wifi.c ::wifi_command (cmd) 开始扫描 wifi_send_command 发出 SCAN 命令调用 wpa_supplicant 开始扫描
扫描完成之后会发送 SCAN_RESULT 在 WifiMonitor 的 HandleEvent 里处理调用 mWifiStateTracker.notifyScanResultsAvailable (); à sendEmptyMessage (EVENT_SCAN_RESULTS_AVAILABLE); mWifiStateTracker 中的 handleMessage 接收到 case EVENT_SCAN_RESULTS_AVAILABLE: 之后发送广播 mContext.sendBroadcast (new Intent (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
WiFiLayer 接收到这个消息在 mReceiver = new BroadcastReceiver () 中处理 handleScanResultsAvailable ();
WiFi 连接流程
用户在 AccessPointDialog 中输入密码之后点击连接按钮,Android 调用顺序如下:
AccessPointDialog.java (packages/apps/settings/src/com/android/settings/wifi) -> onClick -> handleConnect (); -> mWifiLayer.connectToNetwork -> 通过 WifiConfiguration config = findConfiguredNetwork (state); 查看是不是配置过的,如果是就直接使用了,如果不是 config = addConfiguration (state, 0); -> managerEnableNetwork -> mWifiManager.enableNetwork -> mService.enableNetwork -> WifiService. enableNetwork -> WifiNative.enableNetworkCommand -> JNI: android_net_wifi_Wifi.cpp android_net_wifi_enableNetworkCommand 调用 wpa_suppcant 发送相关命令返回之后由 WiFiMonitor 处理跟以前类似,连接的中间流程与查找 AP 的流程类似,都经过了 WifiMonitor 对 “CONNECTED” 消息响应的捕获,以及 WifiStateTracker 对 EVENT_SUPPLICANT_STATE_ CHANGED 的处理。还有一个比较重
要的步骤是 WifiStateTracker 通过对 DHCP 服务器的申请进行了 IP 地址分配。最终会广播 NETWORK_STATE_CHANGED_ ACTION 消息,由 WifiLayer 响应。
IP 地址分配
由上面继续说 IP 地址分配,因为当 wpa_supplicant 链接 AP 成功之后,它会发出事件从而 wifi_for_event 函数会接收到该事件,由 WifiMonitor 中的 MonitorThread 执行执行这个事件 handleEvent-> case CONNECTED: handleNetworkStateChange -> mWifiStateTracker.notifyStateChange -> EVENT_NETWORK_STATE_CHANGED -> handleMessage 下的:case EVENT_SUPPLICANT_STATE_CHANGED: -> intent = new Intent (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION); Wi-Fi supplicant state changed:
èSettingsObserver 专门是观察该类变化的
if (changed) {
resetInterface(true);
configureInterface();
if (mUseStaticIp) {
mTarget.sendEmptyMessage(EVENT_CONFIGURATION_CHANGED);
}
}
è
mDhcpTarget.sendEmptyMessage(EVENT_DHCP_START);
->
DhcpHandler 的 handleMessage 函数 case EVENT_DHCP_START: NetworkUtils.runDhcp 获取 DHCP 的 IP 地址,成功之后发送 EVENT_INTERFACE_CONFIGURATION_SUCCEEDED:
event 通过 WifiStateTracker 的 HandleMessage 函数 case EVENT_INTERFACE_CONFIGURATION_SUCCEEDED: 会调用 sendNetworkStateChangeBroadcast Intent intent = new Intent (WifiManager.NETWORK_STATE_CHANGED_ACTION); 发送全局 Intent Action 完成网络切换。
-------------------------------------------------------------------WIFI 启动 代码流程走读
初始化
在 SystemServer 启动的时候,会生成一个 ConnectivityService 的实例 ,
try {
Log.i(TAG, "Starting Connectivity Service.");
ServiceManager.addService(Context.CONNECTIVITY_SERVICE, new
ConnectivityService(context));
} catch (Throwable e) {
Log.e(TAG, "Failure starting Connectivity Service", e);
}
ConnectivityService 的构造函数 会创建 WifiService,
if (DBG) Log.v(TAG, "Starting Wifi Service.");
mWifiStateTracker = new WifiStateTracker(context, handler);
WifiService wifiService = new WifiService(context, mWifiStateTracker);
ServiceManager.addService(Context.WIFI_SERVICE, wifiService);
WifiStateTracker 会创建 WifiMonitor 接收来自底层的事件,WifiService 和 WifiMonitor 是整
个模块的核心 。WifiService 负责启动关闭 wpa_supplicant、启动关闭 WifiMonitor 监视线程
和把命令 下发给 wpa_supplicant, 而 WifiMonitor 则负责从 wpa_supplicant 接收事件通知。
连接 AP
1. 使能 WIFI
WirelessSettings 在初始化的时候配置了由 WifiEnabler 来处理 Wifi 按钮,
private void initToggles() {
mWifiEnabler = new WifiEnabler(
this,
(WifiManager) getSystemService(WIFI_SERVICE),
(CheckBoxPreference) findPreference(KEY_TOGGLE_WIFI));
当用户按下 Wifi 按钮后,Android 会调用 WifiEnabler 的 onPreferenceChange, 再由 WifiEnabler
调用 WifiManager 的 setWifiEnabled 接口 函数,通过 AIDL, 实际调用的是 WifiService 的
setWifiEnabled 函数,WifiService 接着向自身发送一条 MESSAGE_ENABLE_WIFI 消息,在
处理该消息的代码 中做真正的使能工作:首先装载 WIFI 内核模块 (该模块的位置硬编码为
"/system/lib/modules/wlan.ko" ), 然 后 启 动 wpa_supplicant ( 配 置 文 件 硬 编 码 为
"/data/misc/wifi/wpa_supplicant.conf") 再通过 WifiStateTracker 来启动 WifiMonitor 中的监视
,
线程。
private boolean setWifiEnabledBlocking(boolean enable) {
final int eventualWifiState = enable ? WIFI_STATE_ENABLED :
WIFI_STATE_DISABLED;
updateWifiState(enable ? WIFI_STATE_ENABLING : WIFI_STATE_DISABLING);
if (enable) {
if (!WifiNative.loadDriver()) {
Log.e(TAG, "Failed to load Wi-Fi driver.");
updateWifiState(WIFI_STATE_UNKNOWN);
return false;
}
if (!WifiNative.startSupplicant()) {
WifiNative.unloadDriver();
Log.e(TAG, "Failed to start supplicant daemon.");
updateWifiState(WIFI_STATE_UNKNOWN);
return false;
}
mWifiStateTracker.startEventLoop();
}
// Success!
persistWifiEnabled(enable);
updateWifiState(eventualWifiState);
return true;
}
当使能成功后,会广播发送 WIFI_STATE_CHANGED_ACTION 这个 Intent 通知外界 WIFI
已 经 成 功 使 能 了 。 WifiEnabler 创 建 的 时 候 就 会 向 Android 注 册 接 收
WIFI_STATE_CHANGED_ACTION, 因此它会收到该 Intent, 从而开始扫描。
private void handleWifiStateChanged(int wifiState) {
if (wifiState == WIFI_STATE_ENABLED) {
loadConfiguredAccessPoints();
attemptScan();
}
2. 查找 AP
扫描的入口函数是 WifiService 的 startScan, 它其实也就是往 wpa_supplicant 发送 SCAN 命
令。
static jboolean android_net_wifi_scanCommand(JNIEnv* env, jobject clazz)
{
jboolean result;
// Ignore any error from setting the scan mode.
// The scan will still work.
(void)doBooleanCommand("DRIVER SCAN-ACTIVE", "OK");
result = doBooleanCommand("SCAN", "OK");
(void)doBooleanCommand("DRIVER SCAN-PASSIVE", "OK");
return result;
}
当 wpa_supplicant 处理完 SCAN 命令后,它会向控制通道发送事件通知扫描完成,从而
wifi_wait_for_event 函数会接收到该事件,由此 WifiMonitor 中的 MonitorThread 会被执行来
出来这个事件,
void handleEvent(int event, String remainder) {
case SCAN_RESULTS:
mWifiStateTracker.notifyScanResultsAvailable();
break;
WifiStateTracker 则接着广播发送 SCAN_RESULTS_AVAILABLE_ACTION 这个 Intent
case EVENT_SCAN_RESULTS_AVAILABLE:
mContext.sendBroadcast(new
Intent(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
WifiLayer 注册了接收 SCAN_RESULTS_AVAILABLE_ACTION 这个 Intent, 所以它的相关
处理函数 handleScanResultsAvailable 会被调用,在该函数中,先会去拿到 SCAN 的结果 (最
终是往 wpa_supplicant 发送 SCAN_RESULT 命令并读取返回值来实现的) ,
List<ScanResult> list = mWifiManager.getScanResults();
对每一个扫描返回的 AP,WifiLayer 会调用 WifiSettings 的 onAccessPointSetChanged 函数,
从而最终把该 AP 加到 GUI 显示列表中。
public void onAccessPointSetChanged(AccessPointState ap, boolean added) {
AccessPointPreference pref = mAps.get(ap);
if (added) {
if (pref == null) {
pref = new AccessPointPreference(this, ap);
mAps.put(ap, pref);
} else {
pref.setEnabled(true);
}
mApCategory.addPreference(pref);
}
}
3. 配置 AP 参数
当用户在 WifiSettings 界面上选择了一个 AP 后,会显示配置 AP 参数的一个对话框,
public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference
preference) {
if (preference instanceof AccessPointPreference) {
AccessPointState state = ((AccessPointPreference)
preference).getAccessPointState();
showAccessPointDialog(state, AccessPointDialog.MODE_INFO);
}
}
4. 连接
当用户在 AcessPointDialog 中选择好加密方式和输入密钥之后,再点击连接按钮,Android
就会去连接这个 AP。
private void handleConnect() {
String password = getEnteredPassword();
if (!TextUtils.isEmpty(password)) {
mState.setPassword(password);
}
mWifiLayer.connectToNetwork(mState);
}
WifiLayer 会先检测这个 AP 是不是之前被配置过,这个是通过向 wpa_supplicant 发送
LIST_NETWORK 命令并且比较返回值来实现的,
// Need WifiConfiguration for the AP
WifiConfiguration config = findConfiguredNetwork(state);
如果 wpa_supplicant 没有这个 AP 的配置信息,则会向 wpa_supplicant 发送 ADD_NETWORK
命令来添加该 AP,
if (config == null) {
// Connecting for the first time, need to create it
config = addConfiguration(state,
ADD_CONFIGURATION_ENABLE|ADD_CONFIGURATION_SAVE);
}
ADD_NETWORK 命 令 会 返 回 一 个 ID , WifiLayer 再 用 这 个 返 回 的 ID 作 为 参 数 向
wpa_supplicant 发送 ENABLE_NETWORK 命令,从而让 wpa_supplicant 去连接该 AP。
// Make sure that network is enabled, and disable others
mReenableApsOnNetworkStateChange = true;
if (!mWifiManager.enableNetwork(state.networkId, true)) {
Log.e(TAG, "Could not enable network ID " + state.networkId);
error(R.string.error_connecting);
return false;
}
5. 配置 IP 地址
当 wpa_supplicant 成功连接上 AP 之后,它会向控制通道发送事件通知连接上 AP 了,从而
wifi_wait_for_event 函数会接收到该事件,由此 WifiMonitor 中的 MonitorThread 会被执行来
出来这个事件,
void handleEvent(int event, String remainder) {
case CONNECTED:
handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED,
remainder);
break;
WifiMonitor 再调用 WifiStateTracker 的 notifyStateChange,WifiStateTracker 则接着会往自身
发送 EVENT_DHCP_START 消息来启动 DHCP 去获取 IP 地址,
private void handleConnectedState() {
setPollTimer();
mLastSignalLevel = -1;
if (!mHaveIPAddress & !mObtainingIPAddress) {
mObtainingIPAddress = true;
mDhcpTarget.obtainMessage(EVENT_DHCP_START).sendToTarget();
}
}
然后再广播发送 NETWORK_STATE_CHANGED_ACTION 这个 Intent
case EVENT_NETWORK_STATE_CHANGED:
if (result.state != DetailedState.DISCONNECTED || !mDisconnectPending) {
intent = new
Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
intent.putExt ra(WifiManager.EXTRA_NETWORK_INFO,
mNetworkInfo);
if (result.BSSID != null)
intent.putExtra(WifiManager.EXTRA_BSSID, result.BSSID);
mContext.sendStickyBroadcast(intent);
}
break;
WifiLayer 注册了接收 NETWORK_STATE_CHANGED_ACTION 这个 Intent, 所以它的相关
处理函数 handleNetworkStateChanged 会被调用,
当 DHCP 拿到 IP 地址之后,会再发送 EVENT_DHCP_SUCCEEDED 消息,
private class DhcpHandler extends Handler {
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_DHCP_START:
if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {
event = EVENT_DHCP_SUCCEEDED;
}
WifiLayer 处 理 EVENT_DHCP_SUCCEEDED 消 息,会 再 次 广 播 发 送
NETWORK_STATE_CHANGED_ACTION 这个 Intent, 这次带上完整的 IP 地址信息。
case EVENT_DHCP_SUCCEEDED:
mWifiInfo.setIpAddress(mDhcpInfo.ipAddress);
setDetailedState(DetailedState.CONNECTED);
intent = new
Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
intent.putExtra(WifiManager.EXTRA_NETWORK_INFO, mNetworkInfo);
mContext.sendStickyBroadcast(intent);
break;
至此为止,整个连接过程完成。
问题:
目前的实现不支持 Ad-hoc 方式。<!--++ plugin_code qcomic begin--> <!--++ plugin_code qcomic end-->
---------------------------------------------------------------------------------android 网络连接管理
系统中对网络的判断和选在是在 Connectivityervice 这个服务中来处理的,在系统启动的时候会启动这个系统服务:
系统启动完毕后,ConnectivityService 在系统启动的时候就启动了。
在 android 内部,用 framework/base/core/res/res/values/config.xml 中定义了网络的类型:
<string-array translatable="false" name="networkAttributes">
<item>"default,wifi,0"</item>
<item>"default,mobile,0"</item>
<item>"mms,mobile,1"</item>
<item>"supl,mobile,1"</item>
<item>"dun,mobile,1"</item>
<item>"hipri,mobile,2"</item>
</string-array>
<string-array translatable="false" name="radioAttributes">
<item>"wifi,1,1"</item>
<item>"mobile,0,1"</item>
</string-array>
ConnectivityManager 定义了向对应的字符串常量:
public static final int TYPE_MOBILE = 0;
/**
* The Default WIFI data connection. When active, all data traffic
* will use this connection by default. Should not coexist with other
* default connections.
*/
public static final int TYPE_WIFI = 1;
/**
* An MMS-specific Mobile data connection. This connection may be the
* same as {@link #TYPEMOBILE} but it may be different. This is used
* by applications needing to talk to the carrier''s Multimedia Messaging
* Service servers. It may coexist with default data connections.
* {@hide}
*/
public static final int TYPE_MOBILE_MMS = 2;
/**
* A SUPL-specific Mobile data connection. This connection may be the
* same as {@link #TYPEMOBILE} but it may be different. This is used
* by applications needing to talk to the carrier''s Secure User Plane
* Location servers for help locating the device. It may coexist with
* default data connections.
* {@hide}
*/
public static final int TYPE_MOBILE_SUPL = 3;
/**
* A DUN-specific Mobile data connection. This connection may be the
* same as {@link #TYPEMOBILE} but it may be different. This is used
* by applicaitons performing a Dial Up Networking bridge so that
* the carrier is aware of DUN traffic. It may coexist with default data
* connections.
* {@hide}
*/
public static final int TYPE_MOBILE_DUN = 4;
/**
* A High Priority Mobile data connection. This connection is typically
* the same as {@link #TYPEMOBILE} but the routing setup is different.
* Only requesting processes will have access to the Mobile DNS servers
* and only IP''s explicitly requested via {@link #requestRouteToHost}
* will route over this interface.
*{@hide}
*/
public static final int TYPE_MOBILE_HIPRI = 5;
/** {@hide} */
public static final int MAX_RADIO_TYPE = TYPE_WIFI;
/** {@hide} */
public static final int MAX_NETWORK_TYPE = TYPE_MOBILE_HIPRI;
public static final int DEFAULT_NETWORK_PREFERENCE = TYPE_WIFI;
并设置了默认的网络连接是 TYPE_WIFI.
ConnectivityManager 的方法是通过 AIDL 的使用,调用 ConnectivityService 中的方法来实现的。ConnectivityService 继承了 IConnectivityManage.stub.
在 ConnectivityService 内部,定义了两个类来解析 xml 中的网络类型,类的代码如下:
private class NetworkAttributes {
/**
* Class for holding settings read from resources.
*/
public String mName;
public int mType;
public int mRadio;
public int mPriority;
public NetworkInfo.State mLastState;
public NetworkAttributes(String init) {
String fragments[] = init.split(",");
mName = fragments[0].toLowerCase();
if (fragments[1].toLowerCase().equals("wifi")) {
mRadio = ConnectivityManager.TYPE_WIFI;
} else {
mRadio = ConnectivityManager.TYPE_MOBILE;
}
if (mName.equals("default")) {
mType = mRadio;
} else if (mName.equals("mms")) {
mType = ConnectivityManager.TYPE_MOBILE_MMS;
} else if (mName.equals("supl")) {
mType = ConnectivityManager.TYPE_MOBILE_SUPL;
} else if (mName.equals("dun")) {
mType = ConnectivityManager.TYPE_MOBILE_DUN;
} else if (mName.equals("hipri")) {
mType = ConnectivityManager.TYPE_MOBILE_HIPRI;
}
mPriority = Integer.parseInt(fragments[2]);
mLastState = NetworkInfo.State.UNKNOWN;
}
public boolean isDefault() {
return (mType == mRadio);
}
}
private class RadioAttributes {
public String mName;
public int mPriority;
public int mSimultaneity;
public int mType;
public RadioAttributes(String init) {
String fragments[] = init.split(",");
mName = fragments[0].toLowerCase();
mPriority = Integer.parseInt(fragments[1]);
mSimultaneity = Integer.parseInt(fragments[2]);
if (mName.equals("wifi")) {
mType = ConnectivityManager.TYPE_WIFI;
} else {
mType = ConnectivityManager.TYPE_MOBILE;
}
}
}
并通过一下代码,来给网络分配优先级,
mPriorityList = new int[naStrings.length];
{
int priority = 0; //lowest
int nextPos = naStrings.length-1;
while (nextPos>-1) {
for (int i = 0; i < mNetAttributes.length; i++) {
if(mNetAttributes[i].mPriority == priority) {
mPriorityList[nextPos--] = i;
}
}
priority++;
}
}
mNetRequestersPids =
new ArrayList[ConnectivityManager.MAX_NETWORK_TYPE+1];
for (int i=0; i<=ConnectivityManager.MAX_NETWORK_TYPE; i++) {
mNetRequestersPids[i] = new ArrayList();
}
其中,TYPE_MOBILE_HIPRI 的优先级最高,其次为 TYPE_MOBILE_MMS,TYPE_MOBILE_SUPL,TYPE_MOBILE_DUN,
优先级最低的为 TYPE_WIFI,TYPE_MOBILE。TYPE_WIFI,TYPE_MOBILE 两个网络类型中,TYPE_WIFI 大于 TYPE_MOBILE 的优先级,
在打开 wifi 的连接后,mobile 网络会被关闭。wifi 网络连接关闭后,mobile 网络会重新连接。在处理网络连接的 Handler 的代码中有处理:
private void handleConnect(NetworkInfo info) {
int type = info.getType();
Log.d(TAG, "Got Network Connection Succ from Driver nwtype="+type);
// snapshot isFailover, because sendConnectedBroadcast() resets it
boolean isFailover = info.isFailover();
NetworkStateTracker thisNet = mNetTrackers[type];
// if this is a default net and other default is running
// kill the one not preferred
if (mNetAttributes[type].isDefault()) {
if (mActiveDefaultNetwork != -1 && mActiveDefaultNetwork != type) {
if ((type != mNetworkPreference &&
mNetAttributes[mActiveDefaultNetwork].mPriority >
mNetAttributes[type].mPriority) ||
mNetworkPreference == mActiveDefaultNetwork) {
if(!((SystemProperties.get(CNE.UseCne,"false").equals("true") ||
SystemProperties.get(CNE.UseCne,"false").equals("TRUE"))&&
CNE.isCndUp)) {
// don''t accept this one
if (DBG) Log.v(TAG, "Not broadcasting CONNECT_ACTION " +
"to torn down network " + info.getTypeName());
teardown(thisNet);
}
return;
} else {
// tear down the other
NetworkStateTracker otherNet =
mNetTrackers[mActiveDefaultNetwork];
if (DBG) Log.v(TAG, "Policy requires " +
otherNet.getNetworkInfo().getTypeName() +
" teardown");
if(!((SystemProperties.get(CNE.UseCne,"false").equals("true") ||
SystemProperties.get(CNE.UseCne,"false").equals("TRUE"))&&
CNE.isCndUp)) {
if (DBG) Log.i(TAG, "CNE To support Simultaneous Nws we"+
" will not tear down other nw");
if (!teardown(otherNet)) {
Log.e(TAG, "Network declined teardown request");
return;
}
}
if (isFailover) {
otherNet.releaseWakeLock();
}
}
}
mActiveDefaultNetwork = type;
}
thisNet.setTeardownRequested(false);
thisNet.updateNetworkSettings();
handleConnectivityChange();
sendConnectedBroadcast(info);
}
SystemServer 启动 ConnectivityService,ConnectivityService 启动对网络的监视器。
在 SystemServer 的 run () 函数中,启动 ConnectivityService 的代码:
try {
Log.i(TAG, "Connectivity Service");
connectivity = ConnectivityService.getInstance(context);
ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity);
connectivity.startCne();
} catch (Throwable e) {
Log.e(TAG, "Failure starting Connectivity Service", e);
}
在 ConnectivityService 的构造函数中启动网络监视器的代码:
if (DBG) Log.v(TAG, "Starting Wifi Service.");
WifiStateTracker wst = new WifiStateTracker(context, mHandler);
WifiService wifiService = new WifiService(context, wst);
ServiceManager.addService(Context.WIFI_SERVICE, wifiService);
mNetTrackers[ConnectivityManager.TYPE_WIFI] = wst;
mNetTrackers[ConnectivityManager.TYPE_MOBILE] =
new MobileDataStateTracker(context, mHandler,
ConnectivityManager.TYPE_MOBILE, Phone.APN_TYPE_DEFAULT,
"MOBILE");
mNetTrackers[ConnectivityManager.TYPE_MOBILE_MMS] =
new MobileDataStateTracker(context, mHandler,
ConnectivityManager.TYPE_MOBILE_MMS, Phone.APN_TYPE_MMS,
"MOBILE_MMS");
mNetTrackers[ConnectivityManager.TYPE_MOBILE_SUPL] =
new MobileDataStateTracker(context, mHandler,
ConnectivityManager.TYPE_MOBILE_SUPL, Phone.APN_TYPE_SUPL,
"MOBILE_SUPL");
mNetTrackers[ConnectivityManager.TYPE_MOBILE_DUN] =
new MobileDataStateTracker(context, mHandler,
ConnectivityManager.TYPE_MOBILE_DUN, Phone.APN_TYPE_DUN,
"MOBILE_DUN");
mNetTrackers[ConnectivityManager.TYPE_MOBILE_HIPRI] =
new MobileDataStateTracker(context, mHandler,
ConnectivityManager.TYPE_MOBILE_HIPRI, Phone.APN_TYPE_HIPRI,
"MOBILE_HIPRI");
mNumDnsEntries = 0;
mTestMode = SystemProperties.get("cm.test.mode").equals("true")
&& SystemProperties.get("ro.build.type").equals("eng");
for (NetworkStateTracker t : mNetTrackers)
t.startMonitoring();
// Constructing this starts it too
mWifiWatchdogService = new WifiWatchdogService(context, wst);
在 settings 中可以设置网络连接,比如打开 wifi,打开 bluetooth, 设置 apn 的连接等等,在设置完成后,设置的消息会存在一个数据库中保存,并发送系统消息来广播网络设置的变化。
在网络监视器中捕捉了 settings 中发出的相应的网络广播信息,
网络监视器中注册了 settings 中网络变化的信息,有变化会做相应的处理,并将处理的结果存储在 NetworkInfo 类的一个对象中,在 ConnectivityService 中通过
public NetworkInfo getNetworkInfo (int networkType) 方法可以得知当前 networkType 类型网络的连接情况。
在 app 中,我们可以通过 ConnectivityManager 来获取当前的网络信息,并能指定当前程序需要的网络类型:
ConnectivityManager mCnn = context.getSystemService(context.NONNECTIVITY_SERVICE);
NetworkInfo mNetinfo = mCnn.getActiveNetworkInfo();
mCnn.setNetworkPreference (int preference);// 设定首选网络类型。
假如没有设定,网络类型为系统默认。在 wifi,3G 网络同时存在的情况下,系统会默认的调用 wifi 网络,加载 wifi 的驱动,走 wifi 网络。
Android 打开 WIFI 并快速获取 WIFI 的信息
转载请标明出处:
http://blog.csdn.net/u011974987/article/details/50551580;
本文出自:【Xiho 的博客】
网上关于如何用代码设置 wifi 的文章一搜一大堆,我在此就不再添乱了,我就试着给大家谈一谈我遇到的问题吧。
首先,我在做有系统定制某 App 的 wifi 模块的项目需求时候,系统默认在出厂设置的时候 wifi 是关闭的,当打开连接 wifi(获取 wifi 信息列表)的页面的时候,我要初始化把 wifi 打开,这个时候直接获取 wifi 列表,却迟迟没有数据返回,后来我又监听了
`WifiManager.SCAN_RESULTS_AVAILABLE_ACTION`,
这个广播,收到广播的时候再去获取 wifi 信息,果然,获取到了,可是这时候有个问题,哎妈呀,当 wifi 打开的时候,去调用这个
`mWifiManager.startScan();`
去扫描 wifi ,等收到上面这个广播之后再去获取 wifi,
mScanResults = mWifiManager.getScanResults();
但是这个期间却要等待 12 左右才获取到 wifi 的数据信息,对于一个产品,用户的体验来说,肯定是不合理的,当时,我也找了好多资料,却都没怎么说这方面的内容,苦了好久,也没想明白,因为也不甜熟悉这一块,后来就去 Android 官网查看 Wifi 相关的 API ,收获却不小,但是这个 Bug 还是没头绪,于是就单独写了一个小 Demo 来一步一步测试,贴代码,再详细说明:
我们添加两个按钮,开启 Wifi,关闭 wifi:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.xu.MainActivity" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" />
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/textView1"
android:layout_centerHorizontal="true"
android:layout_marginTop="111dp"
android:text="跳转" />
<Button
android:id="@+id/open_wifi"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/button1"
android:layout_alignLeft="@+id/button1"
android:layout_marginBottom="49dp"
android:text="开启WiFi" />
<Button
android:id="@+id/close_wifi"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/button1"
android:layout_below="@+id/button1"
android:text="关闭wifi" />
</RelativeLayout>
MainActivity:
/** * 打开wifi功能 * true:打开成功; * false:打开失败 */
public boolean openWifi() {
boolean bRet = true;
if (!mWifiManager.isWifiEnabled()) {
bRet = mWifiManager.setWifiEnabled(true);
}
return bRet;
}
/** * Function:关闭wifi * @return<br> */
public boolean closeWifi() {
if (mWifiManager.isWifiEnabled()) {
return mWifiManager.setWifiEnabled(false);
}
return false;
}
当进入这个页面的时候就去注册广播,然后先去手动打开 wifi,调用 openWifi()方法,
/** * 注册广播 */
private void registerBroadcast() {
IntentFilter filter = new IntentFilter();
filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
registerReceiver(mReceiver, filter);
}
打印出相应的 Log 时间差信息:
/** * 打开wifi */
openWifi.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
openWifi();
Log.d(TAG, "开启" + DateUtils.getCurrentDateString());
mWifiManager.startScan();
}
});
/** * 关闭wifi */
closeWifi.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
closeWifi();
Log.d(TAG, "关闭" + DateUtils.getCurrentDateString());
}
});
接收广播的时候也输出相应的日志信息,如果在接收到
WifiManager.SCAN_RESULTS_AVAILABLE_ACTION
这个广播的时候再去扫描 wifi 信息;
mWifiManager.startScan();
mScanResults = mWifiManager.getScanResults();
这个时候去看 log 输出的接收到这个广播的时间,代码如下:
/** * 广播接收,监听网络 */
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
// wifi已成功扫描到可用wifi。
if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
Log.d(TAG, "接收到" +DateUtils.getCurrentDateString());
mWifiManager.startScan();
mScanResults = mWifiManager.getScanResults();
Log.d(TAG, "mScanResults.size()===" + mScanResults.size());
}
}
};
这个时候我就监听了这一个广播,log 日志看图:
看上面的时间差就等候了近 13 秒左右才出来结果,就是为了解决这个问题,又去看有关的资料和 API,后来有添加一个广播。
filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
更改后,在监听这个系统 wifi 打开时候,就去扫描 wifi,
/** * 广播接收,监听网络 */
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
// wifi已成功扫描到可用wifi。
if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
Log.d(TAG, "接收到" +DateUtils.getCurrentDateString());
mScanResults = mWifiManager.getScanResults();
Log.d(TAG, "mScanResults.size()===" + mScanResults.size());
}
//系统wifi的状态
else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
int wifiState = intent.getIntExtra(
WifiManager.EXTRA_WIFI_STATE, 0);
switch (wifiState) {
case WifiManager.WIFI_STATE_ENABLED:
Log.d(TAG, "WiFi已启用" + DateUtils.getCurrentTime());
mWifiManager.startScan(); <<<这里
break;
case WifiManager.WIFI_STATE_DISABLED:
Log.d(TAG, "Wifi已关闭" + DateUtils.getCurrentTime());
break;
}
}
}
};
运行后看下 log 日志:
demo 会放在后面附件里
惊奇的发现,哇哦!这个速度到 wifi 数据显示的时间差就在 2 秒左右哦,非常棒!这个就是我们想要的效果~,当然了,其实还要更多的方法同样能实现这样的效果,本人能力有限,还是自己不够用心,基础不扎实,不够努力,导致了这样,那样的问题。在别人眼里,虽然没什么难度,但是终究自己弄了个小 demo 测试了出来,就把遇到问题就分享给了大家,以后慢慢学会写博客~
等这段时间忙完了,我正整理一下连接 wifi 的相关操作的完整 demo,到时候再更新博客~
Demo 下载:Android 打开 wifi 快速获取 Wifi 信息 Demo
Android 编程实现连接 Wifi (运用 Wifi 相关 API)
/*
* WifiConnect.java
* Author: cscmaker
*/
package com.wifi.connect;
import java.util.List;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.util.Log;
public class WifiConnect {
WifiManager wifiManager;
//定义几种加密方式,一种是WEP,一种是WPA,还有没有密码的情况
public enum WifiCipherType
{
WIFICIPHER_WEP,WIFICIPHER_WPA, WIFICIPHER_NOPASS, WIFICIPHER_INVALID
}
//构造函数
public WifiConnect(WifiManager wifiManager)
{
this.wifiManager = wifiManager;
}
//打开wifi功能
private boolean OpenWifi()
{
boolean bRet = true;
if (!wifiManager.isWifiEnabled())
{
bRet = wifiManager.setWifiEnabled(true);
}
return bRet;
}
//提供一个外部接口,传入要连接的无线网
public boolean Connect(String SSID, String Password, WifiCipherType Type)
{
if(!this.OpenWifi())
{
return false;
}
//开启wifi功能需要一段时间(我在手机上测试一般需要1-3秒左右),所以要等到wifi
//状态变成WIFI_STATE_ENABLED的时候才能执行下面的语句
while(wifiManager.getWifiState() == WifiManager.WIFI_STATE_ENABLING )
{
try{
//为了避免程序一直while循环,让它睡个100毫秒在检测……
Thread.currentThread();
Thread.sleep(100);
}
catch(InterruptedException ie){
}
}
WifiConfiguration wifiConfig = this.CreateWifiInfo(SSID, Password, Type);
//
if(wifiConfig == null)
{
return false;
}
WifiConfiguration tempConfig = this.IsExsits(SSID);
if(tempConfig != null)
{
wifiManager.removeNetwork(tempConfig.networkId);
}
int netID = wifiManager.addNetwork(wifiConfig);
boolean bRet = wifiManager.enableNetwork(netID, false);
return bRet;
}
//查看以前是否也配置过这个网络
private WifiConfiguration IsExsits(String SSID)
{
List<WifiConfiguration> existingConfigs = wifiManager.getConfiguredNetworks();
for (WifiConfiguration existingConfig : existingConfigs)
{
if (existingConfig.SSID.equals("\""+SSID+"\""))
{
return existingConfig;
}
}
return null;
}
private WifiConfiguration CreateWifiInfo(String SSID, String Password, WifiCipherType Type)
{
WifiConfiguration config = new WifiConfiguration();
config.allowedAuthAlgorithms.clear();
config.allowedGroupCiphers.clear();
config.allowedKeyManagement.clear();
config.allowedPairwiseCiphers.clear();
config.allowedProtocols.clear();
config.SSID = "\"" + SSID + "\"";
if(Type == WifiCipherType.WIFICIPHER_NOPASS)
{
config.wepKeys[0] = "";
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
config.wepTxKeyIndex = 0;
}
if(Type == WifiCipherType.WIFICIPHER_WEP)
{
config.preSharedKey = "\""+Password+"\"";
config.hiddenSSID = true;
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP104);
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
config.wepTxKeyIndex = 0;
}
if(Type == WifiCipherType.WIFICIPHER_WPA)
{
config.preSharedKey = "\""+Password+"\"";
config.hiddenSSID = true;
config.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
config.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
config.status = WifiConfiguration.Status.ENABLED;
}
else
{
return null;
}
return config;
}
}
关于赛门铁克首次涉足硬件领域:推出安全 WiFi 路由器和赛门铁克官网下载的问题我们已经讲解完毕,感谢您的阅读,如果还想了解更多关于Android Emulator 不适用于 wifi 路由器,但可以在 macbook pro 中与移动热点配合使用、Android 中的 WiFi 学习笔记 (转载)----WIFI 启动 代码流程走读 --- 网络连接流程、Android 打开 WIFI 并快速获取 WIFI 的信息、Android 编程实现连接 Wifi (运用 Wifi 相关 API)等相关内容,可以在本站寻找。
本文标签: