2010年7月15日 星期四

淺析android下如何通過jni監控wifi網絡連接、dhcpcd執行和power電源控制(轉貼)

=============================================================================================================
libs/android_runtime/android_net_wifi_Wifi.cpp
部分jni接口
static JNINativeMethod gWifiMethods[] = {
{ "loadDriver", "()Z", (void *)android_net_wifi_loadDriver },
{ "setPowerModeCommand", "(I)Z", (void*) android_net_wifi_setPowerModeCommand },//電源管理
{ "connectToSupplicant", "()Z", (void *)android_net_wifi_connectToSupplicant },
{ "waitForEvent", "()Ljava/lang/String;", (void*) android_net_wifi_waitForEvent },
{ "disconnectCommand", "()Z", (void *)android_net_wifi_disconnectCommand },
...
};
int register_android_net_wifi_WifiManager(JNIEnv* env)
{
...
return AndroidRuntime::registerNativeMethods(env,
WIFI_PKG_NAME, gWifiMethods, NELEM(gWifiMethods));//登記jni
}
libs/android_runtime/AndroidRuntime.cpp
static const RegJNIRec gRegJNI[] = {
...
REG_JNI(register_android_net_wifi_WifiManager),
...
};
int AndroidRuntime::startReg(JNIEnv* env)
{
...
register_jni_procs(gRegJNI, NELEM(gRegJNI), env);
...
}
AndroidRuntime::start
=>startReg(env)即調用方法int AndroidRuntime::startReg(JNIEnv* env)
=============================================================================================================
wifi_load_driver
wifi_start_supplicant
=>ensure_config_file_exists
//檢查/data/misc/wifi/wpa_supplicant.conf文件是否存在,如果不存在,那麼從/system/etc/wifi/wpa_supplicant.conf動態拷貝一份
android_net_wifi_connectToSupplicant
=>wifi_connect_to_supplicant
=>
ctrl_conn = wpa_ctrl_open(ifname);
monitor_conn = wpa_ctrl_open(ifname);
wpa_ctrl_attach(monitor_conn);
android_net_wifi_waitForEvent
=>wifi_wait_for_event
=>wpa_ctrl_recv(monitor_conn, buf, &nread);
=>recv(ctrl->s, reply, *reply_len, 0);//阻塞等待wpa_supplicant的netlink數據過來
=>如果接收的buf數據區,buf[0]為'<',那麼說明有level級別信息,所以將'<'...'>'數據剔除,然後wifi_wait_for_event函數返回[luther.gliethttp].
java/android/android/net/wifi/WifiMonitor.java
public class WifiMonitor {
...
public void startMonitoring() {
new MonitorThread().start();//啟動java線程
}
class MonitorThread extends Thread {
public MonitorThread() {
super("WifiMonitor");
}
public void run() {
for (;;) {
ensureSupplicantConnection();//=>WifiNative.connectToSupplicant調用jni函數android_net_wifi_connectToSupplicant
String eventStr = WifiNative.waitForEvent();//=>調用jni函數android_net_wifi_waitForEvent
//private static final int CONNECTED = 1;
//private static final int DISCONNECTED = 2;
//private static final String eventPrefix = "CTRL-EVENT-";
//private static final int eventPrefixLen = eventPrefix.length();
//private static final String connectedEvent = "CONNECTED";
//private static final String disconnectedEvent = "DISCONNECTED";
String eventName = eventStr.substring(eventPrefixLen);//去掉"CTRL-EVENT-"字符串
int nameEnd = eventName.indexOf(' ');//找到隨後的空格位置,這在wpa_supplicant發送時
//#define WPA_EVENT_CONNECTED "CTRL-EVENT-CONNECTED "中,已經內置空格了.
if (nameEnd != -1)
eventName = eventName.substring(0, nameEnd);
int event;
if (eventName.equals(connectedEvent))//檢測netlink過來的字符串action類型
event = CONNECTED;
else if (eventName.equals(disconnectedEvent))
event = DISCONNECTED;
...
int ind = eventStr.indexOf(" - ");//CTRL-EVENT-CONNECTED - Connection to ...
if (ind != -1)
eventData = eventStr.substring(ind + 3);
//剔除前導控制字符,將" - "後面的描述字符串作為真實數據,繼續處理
...
if (event == STATE_CHANGE) {
handleSupplicantStateChange(eventData);
} else if (event == DRIVER_STATE) {
handleDriverEvent(eventData);
} else {
handleEvent(event, eventData);//對於CONNECTED和DISCONNECTED等netlink事件將執行此操作來處理[luther.gliethttp]
// If supplicant is gone, exit the thread
if (event == TERMINATING) {
break;
}
}
...
void handleEvent(int event, String remainder) {
switch (event) {
case DISCONNECTED:
handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED, remainder);
break;
case CONNECTED:
handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED, remainder);//控制界面顯示
break;
...
}
public class WifiStateTracker extends NetworkStateTracker {
...
public void startEventLoop() {
mWifiMonitor.startMonitoring();//啟動上面的MonitorThread線程
}
...
}
java/services/com/android/server/WifiService.java
public class WifiService extends IWifiManager.Stub {
...
private boolean setWifiEnabledBlocking(boolean enable) {
final int eventualWifiState = enable ? WIFI_STATE_ENABLED : WIFI_STATE_DISABLED;
...
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();
//啟動MonitorThread線程,等待wpa_supplicant將netlink數據轉發過來,然後根據netlink動作類型,進一步影響界面顯示[luther.gliethttp].
}
...
}
java/android/android/net/wifi/WifiStateTracker.java
電源管理
private void handleConnectedState() {
...
mDhcpTarget.obtainMessage(EVENT_DHCP_START).sendToTarget();//傳遞到下面的handleMessage方法
...
}
public void onChange(boolean selfChange) {
...
handleConnectedState();
...
}
public class WifiStateTracker extends NetworkStateTracker {
...
public void handleMessage(Message msg) {
switch (msg.what) {
case EVENT_SUPPLICANT_CONNECTION:
case EVENT_NETWORK_STATE_CHANGED:
handleConnectedState();//調用
...
private class DhcpHandler extends Handler {
private Handler mTarget;
public DhcpHandler(Looper looper, Handler target) {
super(looper);
mTarget = target;
}
public void handleMessage(Message msg) {
int event;
//private static final int DRIVER_POWER_MODE_AUTO = 0;
//private static final int DRIVER_POWER_MODE_ACTIVE = 1;
switch (msg.what) {
case EVENT_DHCP_START:
synchronized (this) {
WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_ACTIVE);//設置電源模式,調用 android_net_wifi_setPowerModeCommand
}
Log.d(TAG, "DhcpHandler: DHCP request started");
//libs/android_runtime/android_net_NetUtils.cpp
//static JNINativeMethod gNetworkUtilMethods[] = {
//{ "runDhcp", "(Ljava/lang/String;Landroid/net/DhcpInfo;)Z", (void *)android_net_utils_runDhcp },
// ...
//};
if (NetworkUtils.runDhcp(mInterfaceName, mDhcpInfo)) {//執行dhcp申請ip地址操作
event = EVENT_INTERFACE_CONFIGURATION_SUCCEEDED;
if (LOCAL_LOGD) Log.v(TAG, "DhcpHandler: DHCP request succeeded");
} else {
event = EVENT_INTERFACE_CONFIGURATION_FAILED;
Log.i(TAG, "DhcpHandler: DHCP request failed: " +
NetworkUtils.getDhcpError());
//如果dhcpcd分配ip失敗,那麼Message.obtain(mTarget, event).sendToTarget();將執行
//WifiNative.disconnectCommand();即:static JNINativeMethod gWifiMethods[] = {
//android_net_wifi_disconnectCommand發送"DISCONNECT"字符串[luther.gliethttp]
//然後在wpa_supplicant服務端執行wpa_supplicant_ctrl_iface_process
//wpa_supplicant_disassociate
}
synchronized (this) {
WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_AUTO);
}
Message.obtain(mTarget, event).sendToTarget();
break;
}
}
}
...
/**
* Send the tracker a notification that a connection to the supplicant
* daemon has been established.
*/
//在上面的public class WifiMonitor=>ensureSupplicantConnection
//=>
//while (!supplicantConnected) {
// boolean connected;
//synchronized (mWifiStateTracker) {
//connected = WifiNative.connectToSupplicant();//如果沒有連接成功,那麼while循環嘗試,直到嘗試成功,或者定義了oneShot,僅一次嘗試
//=>mWifiStateTracker.notifySupplicantConnection();//如果WifiNative.connectToSupplicant()成功,那麼將執行
//mWifiStateTracker.notifySupplicantConnection();的調用.
void notifySupplicantConnection() {//向對象發送message
Message.obtain(this, EVENT_SUPPLICANT_CONNECTION).sendToTarget();
}
void notifyStateChange(SupplicantState newState) {
Message.obtain(this, EVENT_SUPPLICANT_STATE_CHANGED, newState).sendToTarget();
}
...
}
static jboolean android_net_wifi_setPowerModeCommand(JNIEnv* env, jobject clazz, jint mode)
{
char cmdstr[256];
sprintf(cmdstr, "DRIVER POWERMODE %d", mode);
return doBooleanCommand(cmdstr, "OK");
}
android_net_wifi_setPowerModeCommand
=>doBooleanCommand
=>doCommand
=>wifi_command
=>wifi_send_command
=>wpa_ctrl_request
=>send給wpa_supplicant
然後wpa_supplicant將做如下接收操作:
system/extra/wpa_supplicant/main.c
=>wpa_supplicant_add_iface
=>wpa_supplicant_init_iface2
=>wpa_supplicant_ctrl_iface_init
=>註冊ctrl_conn控制端口和monitor_conn監聽端口的處理函數
eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive, wpa_s, priv);//ctrl_conn端口的handler處理函數
wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);//monitor_conn端口的回調處理函數,處理netlink數據到所有monitor_conn監聽端口
=>wpa_supplicant_ctrl_iface_receive//對於unix通信方式
=>wpa_supplicant_ctrl_iface_process
=>如果wpa_cli發送的是wpa_cli driver xxx形式的命令,那麼調用這個函數
if (os_strncmp(buf, "DRIVER ", 7) == 0) {//掠過前7個,直接將命令傳過去
reply_len = wpa_supplicant_driver_cmd(wpa_s, buf + 7, reply, reply_size);
=>wpa_supplicant_driver_cmd
=>wpa_drv_driver_cmd
=>自定義DRIVER擴展處理函數,所以對於java傳遞過來的power電源管理命令,wpa_drv_driver_cmd將收到"POWERMODE 0"或者"POWERMODE 1"字符串[luther.gliethttp]
=============================================================================================================
jni
=>runDhcp
=>android_net_utils_runDhcp
libs/netutils/dhcp_utils.c
=>dhcp_do_request
=>
static const char DAEMON_NAME[] = "dhcpcd";
static const char DAEMON_PROP_NAME[] = "init.svc.dhcpcd";
static const char DHCP_PROP_NAME_PREFIX[] = "dhcp";
const char *ctrl_prop = "ctl.start";
const char *desired_status = "running";
snprintf(result_prop_name, sizeof(result_prop_name), "%s.%s.result",
DHCP_PROP_NAME_PREFIX,
interface);
property_set(result_prop_name, "");//設置dhcp.eth0.result="";等到成功完成dhcp之後,
property_set(ctrl_prop, DAEMON_NAME);//向名字為dhcpcd的service,發送"ctrl.start"啟動命令字,該service在init.rc中
//init.rc中dhcpcd服務進程命令字
//service dhcpcd /system/bin/dhcpcd eth0
// disabled
// oneshot
wait_for_property(DAEMON_PROP_NAME, desired_status, 10);
//init.c=>init進程
//=>handle_property_set_fd因為是"ctrl.start"命令字,所以調用handle_control_message處理控制信息
//=>handle_control_message
//=>msg_start
//=>
// struct service *svc = service_find_by_name(name);
// service_start(svc);//啟動svc,即執行:/system/bin/dhcpcd eth0
//=>service_start
//=>pid = fork();
// if(pid == 0)execve(svc->args[0], (char**) svc->args, (char**) ENV);子進程執行execve運行/system/bin/dhcpcd,參數為eth0
//=>否則父進程,即init進程將
//=>notify_service_state(svc->name, "running");設置該svc的狀態prop
// snprintf(pname, sizeof(pname), "init.svc.%s", name);
// property_set(pname, state);//所以這樣上面wait_for_property(DAEMON_PROP_NAME, desired_status, 10);也才能夠正常pass[luther.gliethttp].
wait_for_property(result_prop_name, NULL, 15);//等待dhcp.eth0.result=非空
=============================================================================================================
system/extra/dhcpcd-4.0.0-beta9/dhcpcd.c
dhcpcd
=>main
# define SYSCONFDIR "/system/etc/dhcpcd"
#define PACKAGE "dhcpcd"
# define CONFIG SYSCONFDIR "/" PACKAGE ".conf"
# define LIBEXECDIR "/system/etc/dhcpcd"
# define SCRIPT LIBEXECDIR "/" PACKAGE "-run-hooks"
=>strlcpy(options->script, SCRIPT, sizeof(options->script));//默認的options->script="/system/etc/dhcpcd /dhcpcd-run-hooks"
=>f = fopen(cf ? cf : CONFIG, "r");//如果沒有指定.conf文件,那麼使用默認.conf文件
=>parse_config_line//解析"/system/etc/dhcpcd/dhcpcd.conf"默認配置文件
=>parse_option
=>如果在"/system/etc/dhcpcd/dhcpcd.conf"有"script"這個節
=>那麼執行strlcpy(options->script, oarg, sizeof(options->script));直接拷貝
/*
{"script", required_argument, NULL, 'c'},
{"option", required_argument, NULL, 'o'},
"/system/etc/dhcpcd/dhcpcd.conf"中的部分內容如下:
...
option domain_name_servers, domain_name, domain_search, host_name
...
*/
=>dhcp_run
=>handle_dhcp_packet
=>handle_dhcp
=>bind_dhcp
reason = "TIMEOUT";reason = "BOUND";reason = "REBIND";reason = "RENEW";
system/extra/dhcpcd-4.0.0-beta9/configure.c
=> configure(iface, reason, state->new, state->old, &state->lease, options, 1);
//如果dhcp超時或者dhcp成功,都會調用exec_script來執行腳本,
//執行setprop dhcp.${interface}.result "failed"或者
//執行setprop dhcp.${interface}.result "ok"
=>exec_script(options, iface->name, reason, NULL, old);
=>然後configure_env通過環境變量將reason傳遞到腳本中
int exec_script(const struct options *options, const char *iface, const char *reason,
const struct dhcp_message *dhcpn, const struct dhcp_message *dhcpo)
=>pid = fork();
=>if(pid == 0)execve(options->script, argv, env);//子進程執行腳本,默認"/system/etc/dhcpcd/dhcpcd-run-hooks"
//dhcpcd-run-hooks腳本會根據level值,決定是否執行system/etc/dhcpcd/dhcpcd-hook/*目錄下的相應文件
//我們的系統在該system/etc/dhcpcd/dhcpcd-hook/*目錄下有如下3個文件
//95-configured
//20-dns.conf
//01-test
=>父進程返回while (waitpid(pid, &status, 0) == -1)等待子進程腳本執行完成
system/extra/dhcpcd-4.0.0-beta9/dhcpcd-hooks/20-dns.conf
system/extra/dhcpcd-4.0.0-beta9/dhcpcd-hooks/95-configured
...
setprop dhcp.${interface}.ipaddress "${new_ip_address}"
setprop dhcp.${interface}.result "ok"//設置屬性為ok
setprop dhcp.${interface}.result "failed"
...
=============================================================================================================
inet_init、tcp_prot
sock->ops->sendmsg(iocb, sock, msg, size);
=>inetsw_array[]
=>inet_stream_ops
=>tcp_sendmsg
=============================================================================================================
wpa_cli.c
=>main
=>wpa_cli_interactive
=>wpa_cli_recv_pending(monitor_conn, 0, 0);//阻塞等待wpa_supplicant發送數據過來
=>如果action_monitor為true,那麼將執行一些簡單加工操作,否則將直接將wpa_supplicant發過來的數據打印到console上[luther.gliethttp].

原地址:http://www.wangchao.net.cn/it/detail_60528.html

Android移植: wifi設計原理-源碼分析(轉貼)


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 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.putExtra(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 方式

IP地址的配置(轉貼)

流程如圖:







Wifi連接(轉貼)

具體流程參見以下流程圖:



配置 AP 參數(轉貼)

當用戶在 WifiSettings 界面上選擇了一個AP 後,會顯示配置AP 參數的一個對話框:



查找AP(轉貼)

四:查找AP

Wifi模塊開啟後會對外發送WIFI_STATE_CHANGED_ACTION。
WifiLayer中註冊了Action的Receiver。
當WifiLayer收到此Action後開始scan的流程,具體如下:





當wpa_supplicant 處理完SCAN 命令後,它會向控制通道發送事件通知掃瞄完成,從wifi_wait_for_event 函數會接收到該事件,由此WifiMonitor 中的MonitorThread 會被執行來出來這個事件:




2010年7月14日 星期三

Wifi模塊的啟動(轉貼)



三: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();
}

具體流程如下流程圖所示:







Wifi模塊的初始化(轉貼)

二: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 接收事件通知。


具體流程圖如下:

Wifi介紹(轉貼)

概述

WIFI 就是一種無線聯網的技術,以前通過網線連接電腦,而現在則是通過無線電波來連網;常見的就是一個無線路由器,那麼在這個無線路由器的電波覆蓋的有效範圍都 可以採用WIFI連接方式進行聯網,如果無線路由器連接了一條ADSL線路或者別的上網線路,則又被稱為「熱點」。

運作原理

Wi-Fi 的設置至少需要一個Access Point(ap)和一個或一個以上的client(hi)。AP每100ms將SSID(Service Set Identifier)經由beacons(信號台)封包廣播一次,beacons封包的傳輸速率是1 Mbit/s,並且長度相當的短,所以這個廣播動作對網絡效能的影響不大。因為Wi-Fi規定的最低傳輸速率是1 Mbit/s ,所以確保所有的Wi-Fi client端都能收到這個SSID廣播封包,client 可以借此決定是否要和這一個SSID的AP連線。使用者可以設定要連線到哪一個SSID。Wi-Fi系統總是對用戶端開放其連接標準,並支援漫遊,這就是 Wi-Fi的好處。但亦意味著,一個無線適配器有可能在性能上優於其他的適配器。由於Wi-Fi通過空氣傳送信號,所以和非交換以太網有相同的特點。 近兩年,出現一種WIFI over cable的新方案。此方案屬於EOC(ethernet over cable)中的一種技術。通過將2.4G wifi射頻降頻後在cable中傳輸。此種方案已經在中國大陸小範圍內試商用。

2010年7月11日 星期日

OpenGL Study

上週六跟教授meeting後,教授說要交一份OpenGL的Homework
所以只好去Survey一下了
為了怕之後忘記看過哪些好文
按照慣例把看過得好文貼在Blog上免得以後忘記囉

Jaus Books

Howto Install OpenGL Development Environment(Ubutu)