设为首页 收藏本站
查看: 1161|回复: 0

[经验分享] wifi连接流程分析

[复制链接]
累计签到:1 天
连续签到:1 天
发表于 2015-9-30 12:33:45 | 显示全部楼层 |阅读模式
  Wifi 连接部分
http://blog.iyunv.com/typename/article/details/5879121 DSC0000.gif
  
  当用户选择一个AP时会弹出一个AP参数配置对话框,此对话框会显示当前选择的AP信号强度,若此AP设置了密码则需要用户输入密码才能登录。WifiSettings中的 onPreferenceTreeClick会被调用          @Override
  public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {
  //点击AP响应函数
  if (preference instanceof AccessPoint) {
  mSelected = (AccessPoint) preference;
  showDialog(mSelected, false);
  } else if (preference == mAddNetwork) {
  mSelected = null;
  showDialog(null, true);
  } else if (preference == mNotifyOpenNetworks) {
  Secure.putInt(getContentResolver(),
  Secure.WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON,
  mNotifyOpenNetworks.isChecked() ? 1 : 0);
  } else {
  return super.onPreferenceTreeClick(screen, preference);
  }
  return true;
  }
  
  用户配置好之后点击连接按钮,onClick函数会被调用。
  public void onClick(DialogInterface dialogInterface, int button) {
  //点击连接按钮的响应函数
  if (button == WifiDialog.BUTTON_FORGET && mSelected != null) {
  forget(mSelected.networkId);
  } else if (button == WifiDialog.BUTTON_SUBMIT && mDialog != null) {
  WifiConfiguration config = mDialog.getConfig();
  
  if (config == null) {
  if (mSelected != null && !requireKeyStore(mSelected.getConfig())) {
  connect(mSelected.networkId);
  }
  } else if (config.networkId != -1) {
  if (mSelected != null) {
  mWifiManager.updateNetwork(config);
  saveNetworks();
  }
  } else {
  int networkId = mWifiManager.addNetwork(config);
  if (networkId != -1) {
  mWifiManager.enableNetwork(networkId, false);
  config.networkId = networkId;
  if (mDialog.edit || requireKeyStore(config)) {
  saveNetworks();
  } else {
  connect(networkId);
  }
  }
  }
  }
  
  连接请求部分
  一.Settings的connect函数响应连接,更新网络保存配置,更新设置当前选择的优先级最高,并
  保存。然后通过enableNetwork使得其他网络不可用来进行连接。最后调用WifiManager的
  reconnect函数连接当前选择的网络。
  二.WifiManager的reconnect函数通过AIDL的Binder机制,调用WifiService的reconnect函数
  三.然后会调用 WifiStateTracker的reconnectCommand函数,通过JNI(android_net_wifi_Wifi)的              
  android_net_wifi_reconnectCommand 函数向WPA_WPASUPPLICANT发送 RECONNECT命令。
  四. android_net_wifi_Wifi通过 doCommand(命令名,响应缓冲,响应缓存大小)调用wifi.c中的
  wifi_command函数来发送命令。
  五.最后通过 wpa_ctrl的wpa_ctrl_request函数向控制通道发送连接命令。
  返回请求部分
  六.当连接上之后WPA_SUPPLICANT会向控制通道发送连接成功命令。wifi.c的
  wifi_wait_for_event函数阻塞调用并返回这个命令的字符串(CONNECTED).
  七.而后WifiMonitor会被执行来处理这个事件,WifiMonitor 再调用 WifiStateTracker的
  notifyStateChange,WifiStateTracker 则接着会往自身发送 EVENT_DHCP_START 消息来启动
  DHCP 去获取 IP 地址,然后广播NETWORK_STATE_CHANGED_ACTION消息,最后由
  WifiSettings类来响应,改变状态和界面信息。
  关键函数功能介绍
  一.connect函数功能
  1.updateNetwork:updateNetwork(config)会将当前选择连接的AP配置信息
  信息传递进去,配置信息有(网络ID等)。如果网络ID为-1则重新添加网络配置,然后向
  wpa_supplicant 发送SET_NETWORK命令(即通过这个网络ID设置其他一些相关信息,设置
  SSID,密码等)如果网络配置不为-1则直接执行后面步骤即发送SET_NETWORK命令。
  2.saveNetwork:告诉supplicant保存当前网络配置并更新列表。SaveNetwork会调用WifiService的
  saveConfiguration向wpa_supplicant发送SAVE_CONFIG命令保存当前网络配置信息,
  如果返回false,则向wpa_supplicant重新发送RECONFIGURE命令获取配置信息,如果获取信
  
  息成功后,会Intent一个  NETWORK_IDS_CHANGED_ACTION事件WifiSettings会注册接受
  这个 时间并更新列表。
  3.enableNetwork函数,向系统获取接口名并使得该接口有效。由于之前传递的disableOthers
  为true则向wpa_supplicant发送SELECT_NETWORK(如果传递的为false则发送               
  ENABLE_NETWORK命令),
  4.reconnect函数:连接AP
  
  二.reconnect函数功能:connect函数会调用WifiManager的reconnect然后通过Binder机制调用
  WifiService的reconnect,再由WifiStateTracke调用WifiNative向wpa_supplicant发送
  RECONNECT命令去连接网络,当连接上wpa_supplicant之后会向控制通道发送连接成功的命   
  令,
  wifi_wait_for_event函数阻塞等待该事件的发生,并返回这个命令的字符串(CONNECTED)
  
  三.android_net_wifi_Wifi函数的doCommand函数会调用wifi.c的wifi_command函数将上层的命
  令向wpa_supplicant发送。
  
  四.wifi_wait_for_event函数以阻塞的方式,等待控制通道传递的事件。当有事件传递过来的时候
  该函数会通过wpa_ctrl的wpa_ctrl_recv函数读取该事件,并以字符串形式返回该事件名。
  int wifi_wait_for_event(char *buf, size_t buflen)
  {
  .......
  result = wpa_ctrl_recv(monitor_conn, buf, &nread);
  if (result < 0) {
  LOGD(&quot;wpa_ctrl_recv failed: %s/n&quot;, strerror(errno));
  strncpy(buf, WPA_EVENT_TERMINATING &quot; - recv error&quot;, buflen-1);
  buf[buflen-1] = '/0';
  return strlen(buf);
  }
  buf[nread] = '/0';
  /* LOGD(&quot;wait_for_event: result=%d nread=%d string=/&quot;%s/&quot;/n&quot;, result, nread, buf); */
  /* Check for EOF on the socket */
  if (result == 0 && nread == 0) {
  /* Fabricate an event to pass up */
  LOGD(&quot;Received EOF on supplicant socket/n&quot;);
  strncpy(buf, WPA_EVENT_TERMINATING &quot; - signal 0 received&quot;, buflen-1);
  buf[buflen-1] = '/0';
  return strlen(buf);
  }
  /*
  * Events strings are in the format
  *
  *     <N>CTRL-EVENT-XXX
  *
  * where N is the message level in numerical form (0=VERBOSE, 1=DEBUG,
  * etc.) and XXX is the event name. The level information is not useful
  * to us, so strip it off.
  */
  if (buf[0] == '<') {
  char *match = strchr(buf, '>');
  if (match != NULL) {
  nread -= (match+1-buf);
  memmove(buf, match+1, nread+1);
  }
  }
  return nread;
  }
  
  五.wpa_ctrl_request,通过socket方式向wpa_supplicant发送命令,以select模式阻塞在
  wpa_supplicant发送和接收。
  int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,char *reply, size_t *reply_len,void (*msg_cb)(char *msg, size_t len))
  {
  .......
  res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv);
  if (FD_ISSET(ctrl->s, &rfds)) {
  res = recv(ctrl->s, reply, *reply_len, 0);
  if (res < 0)
  return res;
  if (res > 0 && reply[0] == '<') {
  /* This is an unsolicited message from
  * wpa_supplicant, not the reply to the
  * request. Use msg_cb to report this to the
  * caller. */
  if (msg_cb) {
  /* Make sure the message is nul
  * terminated. */
  if ((size_t) res == *reply_len)
  res = (*reply_len) - 1;
  reply[res] = '/0';
  msg_cb(reply, res);
  }
  continue;
  }
  *reply_len = res;
  break;
  } else {
  return -2;
  }
  }
  return 0;
  }
  
  六.WifiMonitor 维护一个监视线程分发处理底层返回上来的事件
  void handleEvent(int event, String remainder) {
  switch (event) {
  case DISCONNECTED:
  handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED, remainder);
  break;
  case CONNECTED:
  handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED, remainder);
  break;
  case SCAN_RESULTS:
  mWifiStateTracker.notifyScanResultsAvailable();
  break;
  case UNKNOWN:
  break;
  }
  }
  此时返回的事件是CONNECTED因此 handleNetworkStateChange会被调用,验证一下BSSID,重新获得networkId
  ,然后调用WifiStateTracke的notifyStateChange通知状态改变了的消息(EVENT_NETWORK_STATE_CHANGED
  接着处理这个消息,会移除可用网络通告,然后通过 configureInterface()的动态获取IP地址。最后
  发送一个NETWORK_STATE_CHANGED_ACTION Intent,WifiSetings注册了此Intent因此会响应该它。由updateConnectionState函数响应。
  七.updateConnectionState 获取连接信息,更新列表状态,设置为Connected,然后设置当前网络为可用状态
  
  private void updateConnectionState(DetailedState state) {
  /* sticky broadcasts can call this when wifi is disabled */
  if (!mWifiManager.isWifiEnabled()) {
  mScanner.pause();
  return;
  }
  
  if (state == DetailedState.OBTAINING_IPADDR) {
  mScanner.pause();
  } else {
  mScanner.resume();
  }
  
  mLastInfo = mWifiManager.getConnectionInfo();
  if (state != null) {
  mLastState = state;
  }
  
  for (int i = mAccessPoints.getPreferenceCount() - 1; i >= 0; --i) {
  ((AccessPoint) mAccessPoints.getPreference(i)).update(mLastInfo, mLastState);
  }
  
  if (mResetNetworks && (state == DetailedState.CONNECTED ||
  state == DetailedState.DISCONNECTED || state == DetailedState.FAILED)) {
  updateAccessPoints();
  enableNetworks();
  }
  }
  
  
   流程图对应的源代码路径为:
  WifiEnabler,WifiSettings对应的路径如下:
  froyo/packages/apps/Settings/src/com/android/settings/
  
  WifiManager,WifiMonitor,WifiStateTracker,WifiNative.对应的源代码路径如下:
  froyo/frameworrks/base/wifi/java/android/net/wifi/
  
  WifiService 对应代码的位置
  froyo/frameworks/base/services/java/com/android/server/
  
  android_net_wifi_Wifi源代码路径如下:
  froyo/frameworks/base/core/jni/
  
  wifi_command,wifi_wait_for_envent源代码路径如下:
  /hardware/libhardware_legacy/wifi/wifi.c
  
  wpa_ctrl_源代码路径如下:
  /external/wpa_supplicant/wpa_ctrl.c
  
  wpa_supplicant源代码路径如下:
  froyo/external/wpa_supplicant/
  

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-120935-1-1.html 上篇帖子: Linux共享wifi给Android手机 下篇帖子: WIFI基本知识整理(转)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表