Android -- Wifi连接流程分析

当我们在Android手机上通过Settings连接一个AP时,间接调用WifiManager的connect()方法:
[java]  view plain  copy
 print ?
  1. /** 
  2.      * Connect to a network with the given configuration. The network also 
  3.      * gets added to the supplicant configuration. 
  4.      * 
  5.      * For a new network, this function is used instead of a 
  6.      * sequence of addNetwork(), enableNetwork(), saveConfiguration() and 
  7.      * reconnect() 
  8.      * 
  9.      * @param config the set of variables that describe the configuration, 
  10.      *            contained in a {@link WifiConfiguration} object. 
  11.      * @param listener for callbacks on success or failure. Can be null. 
  12.      * @throws IllegalStateException if the WifiManager instance needs to be 
  13.      * initialized again 
  14.      * 
  15.      * @hide 
  16.      */  
  17.     public void connect(WifiConfiguration config, ActionListener listener) {  
  18.         if (config == nullthrow new IllegalArgumentException("config cannot be null");  
  19.         validateChannel();  
  20.         // Use INVALID_NETWORK_ID for arg1 when passing a config object  
  21.         // arg1 is used to pass network id when the network already exists  
  22.         sAsyncChannel.sendMessage(CONNECT_NETWORK, WifiConfiguration.INVALID_NETWORK_ID,  
  23.                 putListener(listener), config);  
  24.     }  
  25.   
  26.     /** 
  27.      * Connect to a network with the given networkId. 
  28.      * 
  29.      * This function is used instead of a enableNetwork(), saveConfiguration() and 
  30.      * reconnect() 
  31.      * 
  32.      * @param networkId the network id identifiying the network in the 
  33.      *                supplicant configuration list 
  34.      * @param listener for callbacks on success or failure. Can be null. 
  35.      * @throws IllegalStateException if the WifiManager instance needs to be 
  36.      * initialized again 
  37.      * @hide 
  38.      */  
  39.     public void connect(int networkId, ActionListener listener) {  
  40.         if (networkId < 0throw new IllegalArgumentException("Network id cannot be negative");  
  41.         validateChannel();  
  42.         sAsyncChannel.sendMessage(CONNECT_NETWORK, networkId, putListener(listener));  
  43.     }  
connect()方法有两种形式,一种接受WifiConfiguration对象,一种接受某个AP的networkID。WifiConfiguration描述了一个Wifi连接的所有配置信息。 
WifiManager的ServiceHandler和WifiService的ClientHandler通过异步通道进行通信。所以这里通过AsyncChannel机制,向WifiServiceImpl发送CONNECT_NETWORK消息,可知在WifiServiceImpl::ClientHandler中被处理:
[java]  view plain  copy
 print ?
  1. /* Client commands are forwarded to state machine */  
  2.                 case WifiManager.CONNECT_NETWORK:  
  3.                 case WifiManager.SAVE_NETWORK: {  
  4.                     WifiConfiguration config = (WifiConfiguration) msg.obj;  
  5.                     int networkId = msg.arg1;  
  6.                     if (msg.what == WifiManager.SAVE_NETWORK) {  
  7.                         Slog.e("WiFiServiceImpl ""SAVE"  
  8.                                 + " nid=" + Integer.toString(networkId)  
  9.                                 + " uid=" + msg.sendingUid  
  10.                                 + " name="  
  11.                                 + mContext.getPackageManager().getNameForUid(msg.sendingUid));  
  12.                     }  
  13.                     if (msg.what == WifiManager.CONNECT_NETWORK) {  
  14.                         Slog.e("WiFiServiceImpl ""CONNECT "  
  15.                                 + " nid=" + Integer.toString(networkId)  
  16.                                 + " uid=" + msg.sendingUid  
  17.                                 + " name="  
  18.                                 + mContext.getPackageManager().getNameForUid(msg.sendingUid));  
  19.                     }  
  20.   
  21.                     if (config != null && isValid(config)) {  
  22.                         if (DBG) Slog.d(TAG, "Connect with config" + config);  
  23.                         mWifiStateMachine.sendMessage(Message.obtain(msg));  
  24.                     } else if (config == null  
  25.                             && networkId != WifiConfiguration.INVALID_NETWORK_ID) {  
  26.                         if (DBG) Slog.d(TAG, "Connect with networkId" + networkId);  
  27.                         mWifiStateMachine.sendMessage(Message.obtain(msg));  
  28.                     } else {  
  29.                         Slog.e(TAG, "ClientHandler.handleMessage ignoring invalid msg=" + msg);  
  30.                         if (msg.what == WifiManager.CONNECT_NETWORK) {  
  31.                             replyFailed(msg, WifiManager.CONNECT_NETWORK_FAILED,  
  32.                                     WifiManager.INVALID_ARGS);  
  33.                         } else {  
  34.                             replyFailed(msg, WifiManager.SAVE_NETWORK_FAILED,  
  35.                                     WifiManager.INVALID_ARGS);  
  36.                         }  
  37.                     }  
  38.                     break;  
  39.                 }  
ClientHandler中并不做具体的连接动作,主要将CONNECT_NETWORK消息被转发到WifiStateMachine中,通过Wifi状态机来驱动连接和DHCP过程 。ConnectModeState处理:
[java]  view plain  copy
 print ?
  1. case WifiManager.CONNECT_NETWORK:  
  2.     /** 
  3.      *  The connect message can contain a network id passed as arg1 on message or 
  4.      * or a config passed as obj on message. 
  5.      * For a new network, a config is passed to create and connect. 
  6.      * For an existing network, a network id is passed 
  7.      */  
  8.     netId = message.arg1;  
  9.     config = (WifiConfiguration) message.obj;  
  10.     mWifiConnectionStatistics.numWifiManagerJoinAttempt++;  
  11.     boolean updatedExisting = false;  
  12.   
  13.     /* Save the network config */  
  14.     if (config != null) {  
  15.         // When connecting to an access point, WifiStateMachine wants to update the  
  16.         // relevant config with administrative data. This update should not be  
  17.         // considered a 'real' update, therefore lockdown by Device Owner must be  
  18.         // disregarded.  
  19.         if (!recordUidIfAuthorized(config, message.sendingUid,  
  20.                 /* onlyAnnotate */ true)) {  
  21.             logw("Not authorized to update network "  
  22.                  + " config=" + config.SSID  
  23.                  + " cnid=" + config.networkId  
  24.                  + " uid=" + message.sendingUid);  
  25.             replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,  
  26.                            WifiManager.NOT_AUTHORIZED);  
  27.             break;  
  28.         }  
  29.   
  30.         String configKey = config.configKey(true /* allowCached */);  
  31.         WifiConfiguration savedConfig =  
  32.                 mWifiConfigStore.getWifiConfiguration(configKey);  
  33.         if (savedConfig != null) {  
  34.             // There is an existing config with this netId, but it wasn't exposed  
  35.             // (either AUTO_JOIN_DELETED or ephemeral; see WifiConfigStore#  
  36.             // getConfiguredNetworks). Remove those bits and update the config.  
  37.             config = savedConfig;  
  38.             logd("CONNECT_NETWORK updating existing config with id=" +  
  39.                     config.networkId + " configKey=" + configKey);  
  40.             config.ephemeral = false;  
  41.             config.autoJoinStatus = WifiConfiguration.AUTO_JOIN_ENABLED;  
  42.             updatedExisting = true;  
  43.         }  
  44.   
  45.         result = mWifiConfigStore.saveNetwork(config, message.sendingUid);  
  46.         netId = result.getNetworkId();  
  47.     }  
  48.     config = mWifiConfigStore.getWifiConfiguration(netId);  
  49.   
  50.     if (config == null) {  
  51.         logd("CONNECT_NETWORK no config for id=" + Integer.toString(netId) + " "  
  52.                 + mSupplicantStateTracker.getSupplicantStateName() + " my state "  
  53.                 + getCurrentState().getName());  
  54.         replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,  
  55.                 WifiManager.ERROR);  
  56.         break;  
  57.     } else {  
  58.         String wasSkipped = config.autoJoinBailedDueToLowRssi ? " skipped" : "";  
  59.         logd("CONNECT_NETWORK id=" + Integer.toString(netId)  
  60.                 + " config=" + config.SSID  
  61.                 + " cnid=" + config.networkId  
  62.                 + " supstate=" + mSupplicantStateTracker.getSupplicantStateName()  
  63.                 + " my state " + getCurrentState().getName()  
  64.                 + " uid = " + message.sendingUid  
  65.                 + wasSkipped);  
  66.     }  
  67.   
  68.     autoRoamSetBSSID(netId, "any");  
  69.   
  70.     if (message.sendingUid == Process.WIFI_UID  
  71.         || message.sendingUid == Process.SYSTEM_UID) {  
  72.         // As a sanity measure, clear the BSSID in the supplicant network block.  
  73.         // If system or Wifi Settings want to connect, they will not  
  74.         // specify the BSSID.  
  75.         // If an app however had added a BSSID to this configuration, and the BSSID  
  76.         // was wrong, Then we would forever fail to connect until that BSSID  
  77.         // is cleaned up.  
  78.         clearConfigBSSID(config, "CONNECT_NETWORK");  
  79.     }  
  80.   
  81.     if (deferForUserInput(message, netId, true)) {  
  82.         break;  
  83.     } else if (mWifiConfigStore.getWifiConfiguration(netId).userApproved ==  
  84.                                                     WifiConfiguration.USER_BANNED) {  
  85.         replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,  
  86.                 WifiManager.NOT_AUTHORIZED);  
  87.         break;  
  88.     }  
  89.   
  90.     mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;  
  91.   
  92.     /* Tell autojoin the user did try to connect to that network if from settings */  
  93.     boolean persist =  
  94.         mWifiConfigStore.checkConfigOverridePermission(message.sendingUid);  
  95.     mWifiAutoJoinController.updateConfigurationHistory(netId, true, persist);  
  96.   
  97.     mWifiConfigStore.setLastSelectedConfiguration(netId);  
  98.   
  99.     didDisconnect = false;  
  100.     if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID  
  101.             && mLastNetworkId != netId) {  
  102.         /** Supplicant will ignore the reconnect if we are currently associated, 
  103.          * hence trigger a disconnect 
  104.          */  
  105.         didDisconnect = true;  
  106.         mWifiNative.disconnect();  
  107.     }  
  108.   
  109.     // Make sure the network is enabled, since supplicant will not reenable it  
  110.     mWifiConfigStore.enableNetworkWithoutBroadcast(netId, false);  
  111.   
  112.     if (mWifiConfigStore.selectNetwork(config, /* updatePriorities = */ true,  
  113.             message.sendingUid) && mWifiNative.reconnect()) {  
  114.         lastConnectAttemptTimestamp = System.currentTimeMillis();  
  115.         targetWificonfiguration = mWifiConfigStore.getWifiConfiguration(netId);  
  116.   
  117.         /* The state tracker handles enabling networks upon completion/failure */  
  118.         mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK);  
  119.         replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);  
  120.         if (didDisconnect) {  
  121.             /* Expect a disconnection from the old connection */  
  122.             transitionTo(mDisconnectingState);  
  123.         } else if (updatedExisting && getCurrentState() == mConnectedState &&  
  124.                 getCurrentWifiConfiguration().networkId == netId) {  
  125.             // Update the current set of network capabilities, but stay in the  
  126.             // current state.  
  127.             updateCapabilities(config);  
  128.         } else {  
  129.             /** 
  130.              *  Directly go to disconnected state where we 
  131.              * process the connection events from supplicant 
  132.              **/  
  133.             transitionTo(mDisconnectedState);  
  134.         }  
  135.     } else {  
  136.         loge("Failed to connect config: " + config + " netId: " + netId);  
  137.         replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,  
  138.                 WifiManager.ERROR);  
  139.         break;  
  140.     }  
  141.     break;  
通过阅读代码,可知主要的处理动作如下: 
  1. 将connect()传过来的AP信息保存到WifiConfigStore对象中
  2. 通过WifiConfigStore::selectNetwork()函数更新WifiConfigStore和config的Priority优先级属性,最后更新到wpa_s配置文件中;enable当前要连接的AP,disable其他的AP
  3. 通过WifiNative::reconnect()函数向wpa_s发送连接指令,连接选定的AP

连接选定的AP是通过调用WifiNative方法向wpa_supplicant发送connect指令,wpa_s通知驱动进行连接;当底层无线连接成功后,framework就能通过WifiMonitor接受到wpa_s上报的event消息:
[java]  view plain  copy
 print ?
  1. /** 
  2.      * Handle all supplicant events except STATE-CHANGE 
  3.      * @param event the event type 
  4.      * @param remainder the rest of the string following the 
  5.      * event name and " — " 
  6.      */  
  7.     void handleEvent(int event, String remainder) {  
  8.         if (DBG) {  
  9.             logDbg("handleEvent " + Integer.toString(event) + "  " + remainder);  
  10.         }  
  11.         switch (event) {  
  12.             case DISCONNECTED:  
  13.                 handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED, remainder);  
  14.                 break;  
  15.   
  16.             case CONNECTED:  
  17.                 handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED, remainder);  
  18.                 break;  
  19.   
  20.             case SCAN_RESULTS:  
  21.                 mStateMachine.sendMessage(SCAN_RESULTS_EVENT);  
  22.                 break;  
  23.   
  24.             case SCAN_FAILED:  
  25.                 mStateMachine.sendMessage(SCAN_FAILED_EVENT);  
  26.                 break;  
  27.   
  28.             case UNKNOWN:  
  29.                 if (DBG) {  
  30.                     logDbg("handleEvent unknown: " + Integer.toString(event) + "  " + remainder);  
  31.                 }  
  32.                 break;  
  33.             default:  
  34.                 break;  
  35.         }  
  36.     }  
[java]  view plain  copy
 print ?
  1. private void handleNetworkStateChange(NetworkInfo.DetailedState newState, String data) {  
  2.      String BSSID = null;  
  3.      int networkId = -1;  
  4.      int reason = 0;  
  5.      int ind = -1;  
  6.      int local = 0;  
  7.      Matcher match;  
  8.      if (newState == NetworkInfo.DetailedState.CONNECTED) {  
  9.          match = mConnectedEventPattern.matcher(data);  
  10.          if (!match.find()) {  
  11.             if (DBG) Log.d(TAG, "handleNetworkStateChange: Couldnt find BSSID in event string");  
  12.          } else {  
  13.              BSSID = match.group(1);  
  14.              try {  
  15.                  networkId = Integer.parseInt(match.group(2));  
  16.              } catch (NumberFormatException e) {  
  17.                  networkId = -1;  
  18.              }  
  19.          }  
  20.          notifyNetworkStateChange(newState, BSSID, networkId, reason);  
  21.      } else if (newState == NetworkInfo.DetailedState.DISCONNECTED) {  
  22.          match = mDisconnectedEventPattern.matcher(data);  
  23.          if (!match.find()) {  
  24.             if (DBG) Log.d(TAG, "handleNetworkStateChange: Could not parse disconnect string");  
  25.          } else {  
  26.              BSSID = match.group(1);  
  27.              try {  
  28.                  reason = Integer.parseInt(match.group(2));  
  29.              } catch (NumberFormatException e) {  
  30.                  reason = -1;  
  31.              }  
  32.              try {  
  33.                  local = Integer.parseInt(match.group(3));  
  34.              } catch (NumberFormatException e) {  
  35.                  local = -1;  
  36.              }  
  37.          }  
  38.          notifyNetworkStateChange(newState, BSSID, local, reason);  
  39.      }  
  40.  }  
  41.   
  42.  /** 
  43.   * Send the state machine a notification that the state of Wifi connectivity 
  44.   * has changed. 
  45.   * @param newState the new network state 
  46.   * @param BSSID when the new state is {@link NetworkInfo.DetailedState#CONNECTED}, 
  47.   * this is the MAC address of the access point. Otherwise, it 
  48.   * is {@code null}. 
  49.   * @param netId the configured network on which the state change occurred 
  50.   */  
  51.  void notifyNetworkStateChange(NetworkInfo.DetailedState newState,  
  52.                                String BSSID, int netId, int reason) {  
  53.      if (newState == NetworkInfo.DetailedState.CONNECTED) {  
  54.          Message m = mStateMachine.obtainMessage(NETWORK_CONNECTION_EVENT,  
  55.                  netId, reason, BSSID);  
  56.          mStateMachine.sendMessage(m);  
  57.      } else {  
  58.   
  59.          Message m = mStateMachine.obtainMessage(NETWORK_DISCONNECTION_EVENT,  
  60.                  netId, reason, BSSID);  
  61.          if (DBG) logDbg("WifiMonitor notify network disconnect: "  
  62.                  + BSSID  
  63.                  + " reason=" + Integer.toString(reason));  
  64.          mStateMachine.sendMessage(m);  
  65.      }  
  66.  }  
WifiMonitor与wpa_s之间的通信是通过socket建立的,如前几篇博客所介绍的那样:WifiMonitor通过建立socket连接与wpa_s通信;每当wpa_s有事件要上报时,WiFiMonitor会解析该event,并转发到Wifi状态机中。 
WifiMonitor接收到wpa_s的连接事件时,向WifiStateMachine发送NETWORK_CONNECTION_EVENT消息,通知状态机底层无线已经连接成功,下一步可以获取IP了。 
转到WifiStateMachine中,ConnectModeState进行处理:
[java]  view plain  copy
 print ?
  1. case WifiMonitor.NETWORK_CONNECTION_EVENT:  
  2.                     if (DBG) log("Network connection established");  
  3.                     mLastNetworkId = message.arg1; //成功加入到某无线网络中的AP的NetworkId  
  4.                     mLastBssid = (String) message.obj;  
  5.   
  6.                     mWifiInfo.setBSSID(mLastBssid);  
  7.                     mWifiInfo.setNetworkId(mLastNetworkId);  
  8.   
  9.                     sendNetworkStateChangeBroadcast(mLastBssid);  
  10.                     transitionTo(mObtainingIpState);  
  11.                     break;  
这里会将这次连接的AP的networkID保存下来,并进入到ObtainingIpState状态去真正触发DHCP动作。L2ConnectedState是ObtainingIpState的父状态,看它的enter()函数:
[java]  view plain  copy
 print ?
  1. public void enter() {  
  2.             mRssiPollToken++;  
  3.             if (mEnableRssiPolling) {  
  4.                 sendMessage(CMD_RSSI_POLL, mRssiPollToken, 0);  
  5.             }  
  6.             if (mNetworkAgent != null) {  
  7.                 loge("Have NetworkAgent when entering L2Connected");  
  8.                 setNetworkDetailedState(DetailedState.DISCONNECTED);  
  9.             }  
  10.             setNetworkDetailedState(DetailedState.CONNECTING);//更新当前的网络连接状态  
  11.   
  12.             if (!TextUtils.isEmpty(mTcpBufferSizes)) {  
  13.                 mLinkProperties.setTcpBufferSizes(mTcpBufferSizes);  
  14.             }  
  15.             mNetworkAgent = new WifiNetworkAgent(getHandler().getLooper(), mContext,  
  16.                     "WifiNetworkAgent", mNetworkInfo, mNetworkCapabilitiesFilter,  
  17.                     mLinkProperties, 60);//此处创建一个NetworkAgent对象用于向ConnectivityService通知相应的网络配置更新操作  
  18.   
  19.             // We must clear the config BSSID, as the wifi chipset may decide to roam  
  20.             // from this point on and having the BSSID specified in the network block would  
  21.             // cause the roam to faile and the device to disconnect  
  22.             clearCurrentConfigBSSID("L2ConnectedState");  
  23.   
  24.             try {  
  25.                 mIpReachabilityMonitor = new IpReachabilityMonitor(  
  26.                         mInterfaceName,  
  27.                         new IpReachabilityMonitor.Callback() {  
  28.                             @Override  
  29.                             public void notifyLost(InetAddress ip, String logMsg) {  
  30.                                 sendMessage(CMD_IP_REACHABILITY_LOST, logMsg);  
  31.                             }  
  32.                         });  
  33.             } catch (IllegalArgumentException e) {  
  34.                 Log.wtf("Failed to create IpReachabilityMonitor", e);  
  35.             }  
  36.         }  
我们再看ObtainingIpState::enter()方法看如何获取获取IP地址:
[java]  view plain  copy
 print ?
  1. @Override  
  2. public void enter() {  
  3.     if (DBG) {  
  4.         String key = "";  
  5.         if (getCurrentWifiConfiguration() != null) {  
  6.             key = getCurrentWifiConfiguration().configKey();  
  7.         }  
  8.         log("enter ObtainingIpState netId=" + Integer.toString(mLastNetworkId)  
  9.                 + " " + key + " "  
  10.                 + " roam=" + mAutoRoaming  
  11.                 + " static=" + mWifiConfigStore.isUsingStaticIp(mLastNetworkId)  
  12.                 + " watchdog= " + obtainingIpWatchdogCount);  
  13.     }  
  14.   
  15.     // Reset link Debouncing, indicating we have successfully re-connected to the AP  
  16.     // We might still be roaming  
  17.     linkDebouncing = false;  
  18.   
  19.     // Send event to CM & network change broadcast  
  20.     setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);  
  21.   
  22.     // We must clear the config BSSID, as the wifi chipset may decide to roam  
  23.     // from this point on and having the BSSID specified in the network block would  
  24.     // cause the roam to faile and the device to disconnect  
  25.     clearCurrentConfigBSSID("ObtainingIpAddress");  
  26.   
  27.     try {  
  28.         mNwService.enableIpv6(mInterfaceName);  
  29.     } catch (RemoteException re) {  
  30.         loge("Failed to enable IPv6: " + re);  
  31.     } catch (IllegalStateException e) {  
  32.         loge("Failed to enable IPv6: " + e);  
  33.     }  
  34.   
  35.     if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {  
  36.         if (isRoaming()) {  
  37.             renewDhcp();  
  38.         } else {  
  39.             // Remove any IP address on the interface in case we're switching from static  
  40.             // IP configuration to DHCP. This is safe because if we get here when not  
  41.             // roaming, we don't have a usable address.  
  42.             clearIPv4Address(mInterfaceName);  
  43.             startDhcp();// DHCP过程启动  
  44.         }  
  45.         obtainingIpWatchdogCount++;  
  46.         logd("Start Dhcp Watchdog " + obtainingIpWatchdogCount);  
  47.         // Get Link layer stats so as we get fresh tx packet counters  
  48.         getWifiLinkLayerStats(true);  
  49.         sendMessageDelayed(obtainMessage(CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER,  
  50.                 obtainingIpWatchdogCount, 0), OBTAINING_IP_ADDRESS_GUARD_TIMER_MSEC);  
  51.     } else {  
  52.         // stop any running dhcp before assigning static IP  
  53.         stopDhcp();  
  54.         StaticIpConfiguration config = mWifiConfigStore.getStaticIpConfiguration(  
  55.                 mLastNetworkId);  
  56.         if (config.ipAddress == null) {  
  57.             logd("Static IP lacks address");  
  58.             sendMessage(CMD_STATIC_IP_FAILURE);  
  59.         } else {  
  60.             InterfaceConfiguration ifcg = new InterfaceConfiguration();  
  61.             ifcg.setLinkAddress(config.ipAddress);  
  62.             ifcg.setInterfaceUp();  
  63.             try {  
  64.                 mNwService.setInterfaceConfig(mInterfaceName, ifcg);  
  65.                 if (DBG) log("Static IP configuration succeeded");  
  66.                 DhcpResults dhcpResults = new DhcpResults(config);  
  67.                 sendMessage(CMD_STATIC_IP_SUCCESS, dhcpResults);  
  68.             } catch (RemoteException re) {  
  69.                 loge("Static IP configuration failed: " + re);  
  70.                 sendMessage(CMD_STATIC_IP_FAILURE);  
  71.             } catch (IllegalStateException e) {  
  72.                 loge("Static IP configuration failed: " + e);  
  73.                 sendMessage(CMD_STATIC_IP_FAILURE);  
  74.             }  
  75.         }  
  76.     }  
  77. }  
这里Wifi分了两种连接方式,Static IP和DHCP。这里区分静态和DHCP是通过WifiConfiguration对象来处理的。WifiConfiguration代表一个配置过的AP连接,主要包含IpAssignment(标识上层的连接方式是静态还是DHCP)、AP的networkID、AP的名字等等。 
我们主要看动态获取IP的过程。进入DHCP流程之前,会先清除当前的IP地址信息。着重看startDhcp()函数处理:
[java]  view plain  copy
 print ?
  1. void startDhcp() {  
  2.     maybeInitDhcpStateMachine(); //确保初始化WifiStateMachine持有的mDhcpStateMachine对象,会传入当前的WifiStateMachine对象,用来回发消息  
  3.     mDhcpStateMachine.registerForPreDhcpNotification();//注册mRegisteredForPreDhcpNotification字段为true,表明我们在发送DHCP包之前需要做一些准备工作  
  4.     mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);//发送开始DHCP的消息  
  5. }  
DhcpStateMachine是一个小状态机,它主要处理DHCP下的IP地址获取过程,并将获取到的DHCP结果告知WifiStateMachine。看DhcpStateMachine处理该消息的过程:
[java]  view plain  copy
 print ?
  1. case CMD_START_DHCP:  
  2.                     if (mRegisteredForPreDhcpNotification) {  
  3.                         /* Notify controller before starting DHCP */  
  4.                         mController.sendMessage(CMD_PRE_DHCP_ACTION);  
  5.                         transitionTo(mWaitBeforeStartState);  
  6.                     } else {  
  7.                         if (runDhcpStart()) {  
  8.                             transitionTo(mRunningState);  
  9.                         }  
  10.                     }  
  11.                     break;  
由于我们之前设置了mRegisteredForPreDhcpNotification为true,这里会向WifiStateMachine发送CMD_PRE_DHCP_ACTION消息,告知Wifi状态机做一些DHCP之前的预处理工作。L2ConnectedState处理该消息:
[java]  view plain  copy
 print ?
  1. case DhcpStateMachine.CMD_PRE_DHCP_ACTION:  
  2.                   handlePreDhcpSetup();  
  3.                   break;  
[java]  view plain  copy
 print ?
  1. void handlePreDhcpSetup() {  
  2.     mDhcpActive = true;  
  3.     if (!mBluetoothConnectionActive) {  
  4.         /* 
  5.          * There are problems setting the Wi-Fi driver's power 
  6.          * mode to active when bluetooth coexistence mode is 
  7.          * enabled or sense. 
  8.          * <p> 
  9.          * We set Wi-Fi to active mode when 
  10.          * obtaining an IP address because we've found 
  11.          * compatibility issues with some routers with low power 
  12.          * mode. 
  13.          * <p> 
  14.          * In order for this active power mode to properly be set, 
  15.          * we disable coexistence mode until we're done with 
  16.          * obtaining an IP address.  One exception is if we 
  17.          * are currently connected to a headset, since disabling 
  18.          * coexistence would interrupt that connection. 
  19.          */  
  20.         // Disable the coexistence mode  
  21.         mWifiNative.setBluetoothCoexistenceMode(  
  22.                 mWifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);  
  23.     }  
  24.   
  25.     // Disable power save and suspend optimizations during DHCP  
  26.     // Note: The order here is important for now. Brcm driver changes  
  27.     // power settings when we control suspend mode optimizations.  
  28.     // TODO: Remove this comment when the driver is fixed.  
  29.     setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, false);  
  30.     mWifiNative.setPowerSave(false);  
  31.   
  32.     // Update link layer stats  
  33.     getWifiLinkLayerStats(false);  
  34.   
  35.     /* P2p discovery breaks dhcp, shut it down in order to get through this */  
  36.     Message msg = new Message();  
  37.     msg.what = WifiP2pServiceImpl.BLOCK_DISCOVERY;  
  38.     msg.arg1 = WifiP2pServiceImpl.ENABLED;  
  39.     msg.arg2 = DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE;  
  40.     msg.obj = mDhcpStateMachine;  
  41.     mWifiP2pChannel.sendMessage(msg);  
  42. }  
从注释可知,为了保证Wifi DHCP过程的顺利进行,对Bluetooth、Power和P2p都做了处理工作,其中:蓝牙会Disable the coexistence mode;停止p2p的discovery过程,防止影响DHCP流程。MD_PRE_DHCP_ACTION_COMPLETE消息会在WifiP2pServiceImpl设置完p2p部分后,被转发到DhcpStateMachine,告知预处理工作已经结束,可以进行DHCP了。看DhcpStateMachine中的消息处理:
[java]  view plain  copy
 print ?
  1. case CMD_PRE_DHCP_ACTION_COMPLETE:  
  2.                     if (runDhcpStart()) {  
  3.                         transitionTo(mRunningState);  
  4.                     } else {  
  5.                         transitionTo(mPollingState);  
  6.                     }  
  7.                     break;  
[java]  view plain  copy
 print ?
  1. private boolean runDhcpStart() {  
  2.     /* Stop any existing DHCP daemon before starting new */  
  3.     NetworkUtils.stopDhcp(mInterfaceName);//在启动新的DHCP流程之前,停止当前正在进行的DHCP过程  
  4.     mDhcpResults = null;  
  5.   
  6.     if (DBG) Log.d(TAG, "DHCP request on " + mInterfaceName);  
  7.     if (!NetworkUtils.startDhcp(mInterfaceName) || !dhcpSucceeded()) {  
  8.         Log.e(TAG, "DHCP request failed on " + mInterfaceName + ": " +  
  9.                 NetworkUtils.getDhcpError());  
  10.         mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_FAILURE, 0)  
  11.                 .sendToTarget();  
  12.         return false;  
  13.     }  
  14.     return true;  
  15. }  
调用NetworkUtils.startDhcp()方法启动DHCP流程去获取IP地址,调用dhcpSucceeded()方法获取该次DHCP的DhcpResults对象,它包含了IP地址、网关、DNS等等地址信息。
[java]  view plain  copy
 print ?
  1. private boolean dhcpSucceeded() {  
  2.         DhcpResults dhcpResults = new DhcpResults();  
  3.         if (!NetworkUtils.getDhcpResults(mInterfaceName, dhcpResults)) {  
  4.             return false;  
  5.         }  
  6.   
  7.         if (DBG) Log.d(TAG, "DHCP results found for " + mInterfaceName);  
  8.         long leaseDuration = dhcpResults.leaseDuration; //int to long conversion  
  9.   
  10.         //Sanity check for renewal  
  11.         if (leaseDuration >= 0) {  
  12.             //TODO: would be good to notify the user that his network configuration is  
  13.             //bad and that the device cannot renew below MIN_RENEWAL_TIME_SECS  
  14.             if (leaseDuration < MIN_RENEWAL_TIME_SECS) {  
  15.                 leaseDuration = MIN_RENEWAL_TIME_SECS;  
  16.             }  
  17.             //Do it a bit earlier than half the lease duration time  
  18.             //to beat the native DHCP client and avoid extra packets  
  19.             //48% for one hour lease time = 29 minutes  
  20.             mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,  
  21.                     SystemClock.elapsedRealtime() +  
  22.                     leaseDuration * 480//in milliseconds  
  23.                     mDhcpRenewalIntent);  
  24.         } else {  
  25.             //infinite lease time, no renewal needed  
  26.         }  
  27.   
  28.         // Fill in any missing fields in dhcpResults from the previous results.  
  29.         // If mDhcpResults is null (i.e. this is the first server response),  
  30.         // this is a noop.  
  31.         dhcpResults.updateFromDhcpRequest(mDhcpResults);  
  32.         mDhcpResults = dhcpResults;  
  33.         mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, dhcpResults)  
  34.             .sendToTarget();  
  35.         return true;  
  36.     }  
如果getDhcpResults()函数执行成功,DhcpStateMachine就会发送CMD_POST_DHCP_ACTION消息给WifiStateMachine,状态位是DHCP_SUCCESS,并附加runDhcp()获取到的DhcpResult对象;失败时则附加DHCP_FAILURE。L2ConnectedState处理CMD_POST_DHCP_ACTION消息:
[java]  view plain  copy
 print ?
  1. case DhcpStateMachine.CMD_POST_DHCP_ACTION:  
  2.                   handlePostDhcpSetup();  
  3.                   if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {  
  4.                       if (DBG) log("DHCP successful");  
  5.                       handleIPv4Success((DhcpResults) message.obj, DhcpStateMachine.DHCP_SUCCESS);  
  6.                       // We advance to mConnectedState because handleIPv4Success will call  
  7.                       // updateLinkProperties, which then sends CMD_IP_CONFIGURATION_SUCCESSFUL.  
  8.                   } else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {  
  9.                       mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_DHCP_FAILURE);  
  10.                       if (DBG) {  
  11.                           int count = -1;  
  12.                           WifiConfiguration config = getCurrentWifiConfiguration();  
  13.                           if (config != null) {  
  14.                               count = config.numConnectionFailures;  
  15.                           }  
  16.                           log("DHCP failure count=" + count);  
  17.                       }  
  18.                       handleIPv4Failure(DhcpStateMachine.DHCP_FAILURE);  
  19.                       // As above, we transition to mDisconnectingState via updateLinkProperties.  
  20.                   }  
  21.                   break;  
首先调用handlePostDhcpSetup()重置之前所做的预处理动作;再处理附加的状态位,成功则调用handleIPv4Success()更新网络的配置信息:
[java]  view plain  copy
 print ?
  1. private void handleIPv4Success(DhcpResults dhcpResults, int reason) {  
  2.   
  3.         if (PDBG) {  
  4.             logd("handleIPv4Success <" + dhcpResults.toString() + ">");  
  5.             logd("link address " + dhcpResults.ipAddress);  
  6.         }  
  7.   
  8.         Inet4Address addr;  
  9.         synchronized (mDhcpResultsLock) {  
  10.             mDhcpResults = dhcpResults;//保存当前的DHCP结果  
  11.             addr = (Inet4Address) dhcpResults.ipAddress.getAddress();  
  12.         }  
  13.   
  14.         if (isRoaming()) {  
  15.             int previousAddress = mWifiInfo.getIpAddress();  
  16.             int newAddress = NetworkUtils.inetAddressToInt(addr);  
  17.             if (previousAddress != newAddress) {  
  18.                 logd("handleIPv4Success, roaming and address changed" +  
  19.                         mWifiInfo + " got: " + addr);  
  20.             }  
  21.         }  
  22.         mWifiInfo.setInetAddress(addr);  
  23.         mWifiInfo.setMeteredHint(dhcpResults.hasMeteredHint());  
  24.         updateLinkProperties(reason);//更新网络配置信息  
  25.     }  
updateLinkProperties()更新当前的网络配置信息:
[java]  view plain  copy
 print ?
  1. private void updateLinkProperties(int reason) {  
  2.     LinkProperties newLp = makeLinkProperties();//根据DHCP的结果创建新的LinkProperties对象,用于对比DHCP前后的网络配置是否已经改变  
  3.   
  4.     final boolean linkChanged = !newLp.equals(mLinkProperties);  
  5.     final boolean wasProvisioned = isProvisioned(mLinkProperties);  
  6.     final boolean isProvisioned = isProvisioned(newLp);  
  7.     // TODO: Teach LinkProperties how to understand static assignment  
  8.     // and simplify all this provisioning change detection logic by  
  9.     // unifying it under LinkProperties.compareProvisioning().  
  10.     final boolean lostProvisioning =  
  11.             (wasProvisioned && !isProvisioned) ||  
  12.             (mLinkProperties.hasIPv4Address() && !newLp.hasIPv4Address()) ||  
  13.             (mLinkProperties.isIPv6Provisioned() && !newLp.isIPv6Provisioned());  
  14.     final DetailedState detailedState = getNetworkDetailedState();  
  15.   
  16.     if (linkChanged) { //网络配置信息改变  
  17.         if (DBG) {  
  18.             log("Link configuration changed for netId: " + mLastNetworkId  
  19.                     + " old: " + mLinkProperties + " new: " + newLp);  
  20.         }  
  21.         mLinkProperties = newLp;//将新的配置信息保存到该字段中  
  22.         if (mIpReachabilityMonitor != null) {  
  23.             mIpReachabilityMonitor.updateLinkProperties(mLinkProperties);  
  24.         }  
  25.         if (mNetworkAgent != null) mNetworkAgent.sendLinkProperties(mLinkProperties);//通过NetworkAgent对象通知ConnectifyService更新网络配置信息  
  26.     }  
  27.   
  28.     if (lostProvisioning) {  
  29.         log("Lost IP layer provisioning!" +  
  30.                 " was: " + mLinkProperties +  
  31.                 " now: " + newLp);  
  32.     }  
  33.   
  34.     // If we just configured or lost IP configuration, do the needful.  
  35.     // We don't just call handleSuccessfulIpConfiguration() or handleIpConfigurationLost()  
  36.     // here because those should only be called if we're attempting to connect or already  
  37.     // connected, whereas updateLinkProperties can be called at any time.  
  38.     switch (reason) {  
  39.         case DhcpStateMachine.DHCP_SUCCESS:  
  40.         case CMD_STATIC_IP_SUCCESS:  
  41.             // IPv4 provisioning succeded. Advance to connected state.  
  42.             sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);  
  43.             if (!isProvisioned) {  
  44.                 // Can never happen unless DHCP reports success but isProvisioned thinks the  
  45.                 // resulting configuration is invalid (e.g., no IPv4 address, or the state in  
  46.                 // mLinkProperties is out of sync with reality, or there's a bug in this code).  
  47.                 // TODO: disconnect here instead. If our configuration is not usable, there's no  
  48.                 // point in staying connected, and if mLinkProperties is out of sync with  
  49.                 // reality, that will cause problems in the future.  
  50.                 logd("IPv4 config succeeded, but not provisioned");  
  51.             }  
  52.             break;  
  53.   
  54.         case DhcpStateMachine.DHCP_FAILURE:  
  55.             // DHCP failed. If we're not already provisioned, or we had IPv4 and now lost it,  
  56.             // give up and disconnect.  
  57.             // If we're already provisioned (e.g., IPv6-only network), stay connected.  
  58.             if (!isProvisioned || lostProvisioning) {  
  59.                 sendMessage(CMD_IP_CONFIGURATION_LOST);  
  60.             } else {  
  61.                 // DHCP failed, but we're provisioned (e.g., if we're on an IPv6-only network).  
  62.                 sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);  
  63.   
  64.                 // To be sure we don't get stuck with a non-working network if all we had is  
  65.                 // IPv4, remove the IPv4 address from the interface (since we're using DHCP,  
  66.                 // and DHCP failed). If we had an IPv4 address before, the deletion of the  
  67.                 // address  will cause a CMD_UPDATE_LINKPROPERTIES. If the IPv4 address was  
  68.                 // necessary for provisioning, its deletion will cause us to disconnect.  
  69.                 //  
  70.                 // This shouldn't be needed, because on an IPv4-only network a DHCP failure will  
  71.                 // have empty DhcpResults and thus empty LinkProperties, and isProvisioned will  
  72.                 // not return true if we're using DHCP and don't have an IPv4 default route. So  
  73.                 // for now it's only here for extra redundancy. However, it will increase  
  74.                 // robustness if we move to getting IPv4 routes from netlink as well.  
  75.                 loge("DHCP failure: provisioned, clearing IPv4 address.");  
  76.                 if (!clearIPv4Address(mInterfaceName)) {  
  77.                     sendMessage(CMD_IP_CONFIGURATION_LOST);  
  78.                 }  
  79.             }  
  80.             break;  
  81.   
  82.         case CMD_STATIC_IP_FAILURE:  
  83.             // Static configuration was invalid, or an error occurred in applying it. Give up.  
  84.             sendMessage(CMD_IP_CONFIGURATION_LOST);  
  85.             break;  
  86.   
  87.         case CMD_UPDATE_LINKPROPERTIES:  
  88.             // IP addresses, DNS servers, etc. changed. Act accordingly.  
  89.             if (lostProvisioning) {  
  90.                 // We no longer have a usable network configuration. Disconnect.  
  91.                 sendMessage(CMD_IP_CONFIGURATION_LOST);  
  92.             } else if (!wasProvisioned && isProvisioned) {  
  93.                 // We have a usable IPv6-only config. Advance to connected state.  
  94.                 sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);  
  95.             }  
  96.             if (linkChanged && getNetworkDetailedState() == DetailedState.CONNECTED) {  
  97.                 // If anything has changed and we're already connected, send out a notification.  
  98.                 sendLinkConfigurationChangedBroadcast();  
  99.             }  
  100.             break;  
  101.     }  
  102. }  
最后会根据DHCP是否成功发送CMD_IP_CONFIGURATION_SUCCESSFUL消息表明IP的配置已经完成(因为我们已经告知了ConnectifyService去更新网络配置,并附加了新的mLinkProperties对象),L2ConnectedState处理CMD_IP_CONFIGURATION_SUCCESSFUL消息:
[java]  view plain  copy
 print ?
  1. case CMD_IP_CONFIGURATION_SUCCESSFUL:  
  2.                     handleSuccessfulIpConfiguration();  
  3.                     sendConnectedState();  
  4.                     transitionTo(mConnectedState);  
handleSuccessfulIpConfiguration()函数处理:
[java]  view plain  copy
 print ?
  1. private void handleSuccessfulIpConfiguration() {  
  2.         mLastSignalLevel = -1// Force update of signal strength  
  3.         WifiConfiguration c = getCurrentWifiConfiguration();//获取代表当前连接的网络的WifiConfiguration对象  
  4.         if (c != null) {  
  5.             // Reset IP failure tracking  
  6.             c.numConnectionFailures = 0;  
  7. //重置numConnectionFailures字段,因为在WifiConfigStore中,会根据numConnectionFailures字段判断当前网络的连接失败次数,默认失败10次时,会终止重连操作  
  8.             // Tell the framework whether the newly connected network is trusted or untrusted.  
  9.             updateCapabilities(c);  
  10.         }  
  11.         if (c != null) {  
  12.             ScanResult result = getCurrentScanResult();  
  13.             if (result == null) {  
  14.                 logd("WifiStateMachine: handleSuccessfulIpConfiguration and no scan results" +  
  15.                         c.configKey());  
  16.             } else {  
  17.                 // Clear the per BSSID failure count  
  18.                 result.numIpConfigFailures = 0;  
  19.                 // Clear the WHOLE BSSID blacklist, which means supplicant is free to retry  
  20.                 // any BSSID, even though it may already have a non zero ip failure count,  
  21.                 // this will typically happen if the user walks away and come back to his arrea  
  22.                 // TODO: implement blacklisting based on a timer, i.e. keep BSSID blacklisted  
  23.                 // in supplicant for a couple of hours or a day  
  24.                 mWifiConfigStore.clearBssidBlacklist();  
  25.             }  
  26.         }  
  27.     }  
sendConnectedState()主要做一些网络状态的更新操作,发送NETWORK_STATE_CHANGED_ACTION通知当前的网络状态已经变化。此广播会附加WifiStateMachine中三个常用的字段值:
[java]  view plain  copy
 print ?
  1. /** 
  2.  * The link properties of the wifi interface. 
  3.  * Do not modify this directly; use updateLinkProperties instead. 
  4.  */  
  5. private LinkProperties mLinkProperties; //保存当前网络连接的配置信息,包括IP地址集、DNS地址集、路由地址集等  
[java]  view plain  copy
 print ?
  1. // NOTE: Do not return to clients - use #getWiFiInfoForUid(int)  
  2. private WifiInfo mWifiInfo;//描述了一个Wifi连接的状态,通过该对象我们可以得知当前Wifi连接的SSID、netId、IP地址、Mac地址等信息  
  3. private NetworkInfo mNetworkInfo;//表示一个网络接口(wlan0/eth0)的连接状态,通过该对象可以得知当前Wifi连接处于哪一步、是否连接等状态  
最后切换到ConnectedState,至此AP已经连接、IP也已获取到,并且网络状态也已更新。后续的过程貌似没什么大用了。一个Wifi的连接流程到这里就分析完了。 
PS: 
4.4之后Android加入了AP评分机制。这个机制根据某个AP的很多配置信息一通处理,最后得出一个score,来表明某个AP的可连接性。它评判的属性有点多,后面在研究,现在本渣还是没有搞明白,囧.... 
在framework里面,其实屏蔽了很多复杂的操作,比如WifiNative里面的大多数方法都是要通过JNI调用wifi.c,再调到wpa_s。这些中间过程其实比framework的处理要复杂的多,代码也更晦涩。例如runDhcp()动作,有个专门的dhcpcd守护进程进行处理;它的实现跟wpa_s一样,都是很复杂的。即使了解DHCP协议的工作流程,要看懂它的代码实现也很痛苦。Android里还有的PPPoE拨号方式,它的守护进程pppd的实现也很复杂......;要做这方面的定制,对于偏向framework的人来说,是比较痛苦的。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
7 Android的Audio系统 7.1 Audio系统的综述 Audio系统架构和代码路径 7.2 meida库中Audio相关接口 Audio系统的本地核心接口,类的层次结构 7.3 Audio系统和上层接口 Audio系统的JAVA层次的接口 7.4 Audio硬件抽象层 Audio系统的移植,Audio硬件抽象层的实现方法 ALSA Audio HAL实现 8 Android的Video 输入输出系统 8.1 Video输入输出系统的综述 视频输入输出系统架构和代码路径 8.2 Overlay系统 视频输出系统的结构 8.3 Overlay的硬件抽象层 视频输出系统的移植,抽象层实现 8.4 Camera系统与上层接口 Camera系统的层次结构 8.5 Camera的硬件抽象层 Camera系统的移植 Camera和Overlay的结合与数据传送 9 Android的多媒体系统 9.1 多媒体系统的结构 应用到底层的层次结构 9.2 media核心库的结构 多媒体本地代码的结构 9.3 多媒体部分的上层代码 照相机,媒体播放,媒体录制的框架结构 9.4 PackageVideo的架构 OpenMAX编译码插件的集成 9.5 stagefright的架构与实现(Éclair新增) 10 Android的电话系统 10.1 电话系统结构 Android电话系统的层次结构 10.2 电话系统组成部分 Android电话系统的本地和JAVA程序运行流程 10.3 电话系统移植层 为特定Modem移植的方法 11 Android连接部分 11.1 WIFI无线局域网的部分 11.2 蓝牙部分 11.3 GPS部分 12 Android的传感器系统 12.1 Android传感器系统概述 12.2 Android传感器系统的层次结构 12.3 Android传感器系统的硬件抽象层和移植 重力加速度等传感器的移植 12.4 Android传感器系统的使用 1.HAL 技术详解 1.1 HAL 架構解析 1.2 Service与Manager的意义与用途 1.3 libhardware 与 HAL API 1.4 Stub & Module 的观念 1.5 专题讨论:定义并撰写第一个HAL Stub 2.HAL Development 2.1 HAL Stub Analysis and Design (OOAD) 2.2 HAL Stub Class 2.3 HAL Stub Interface 2.4 专题讨论:开发 LED 的 HAL 模组 3.Android API Design 3.1 Abstract Class and Interface in Android 3.2 The Reuse of Framework Design 3.3 OOAD of new APIs 3.4 Implementaion of new APIs 3.5 Singleton Pattern to Android APIs 3.6 Factory Method Pattern to Android APIs 4.Extend Android API 4.1 如何加入 API 至 Android Framework 4.2 如何编译并制作独立 JAR 档 4.3 上机实验:开发LedService API 与制作mokoid.jar 程式库 5.JNI & Runtime Library 5.1 什么是 JNI 5.2 如何撰写 JNI & Native Method 5.3 如何制作 Android Runtime Library 5.4 专题讨论:如何开发与制作Runtime Library 6.5 专题讨论:SensorManager与SensorService实例 7.SystemService 与 HAL 整合7.1 IPC、 Remote method call与Binder观念说明 7.2 AIDL 介绍与IInterface设计观念解析 7.3 Activity & ApplicationContext 7.4 ServiceManager 7.5 专题讨论:LedService设计与ILedService探讨 8.Manager API (Refinement-架构优化) 8.1 SensorManager与 SensorService实例探讨 8.2 Remote Object观念与IBinder介绍 8.3 如何以Proxy Object整合Android Service 8.4 Long operations 的解析与实作细节 8.5 RemoteException 的解析与实作细节 8.6 Handler 与 Message 的解析与实作细节 8.7 Error Handling 9.专题探讨 9.1 整合驱动程式至Android 框架 9.2 撰写Anroid应用程式以控制LED 9.3 标准的Android HAL 架构 9.4 HAL Stub OOAD & Implementation
AndroidWiFi连接流程图主要包括以下步骤: 1. 打开WiFi:用户进入设置界面,打开设备的WiFi开关,使设备开始搜索可用的WiFi网络。 2. 搜索和扫描:Android设备开始搜索周围的WiFi网络,扫描到的网络信息将会显示在WiFi设置列表中,包括网络名称(SSID)、信号强度等。 3. 用户选择网络:用户根据列表中显示的网络信息,选择要连接WiFi网络。 4. 请求连接Android设备通过向选择的WiFi网络发送请求连接的请求,包括认证信息和其他必要的参数。 5. 连接认证:WiFi网络收到连接请求后,会进行身份验证,包括密码验证、MAC地址过滤等。如果认证通过,网络将发送认可的连接响应。 6. IP地址获取:一旦连接成功,Android设备将向WiFi网络请求分配一个IP地址,以便在网络上进行通信。 7. 地址分配:WiFi网络会为设备分配一个合法的IP地址,并将该信息通过DHCP(动态主机配置协议)返回给设备。 8. 连接成功:设备接收到IP地址和其他网络配置信息后,与WiFi网络建立连接成功。 9. 数据传输:现在,设备可以通过WiFi网络进行数据传输,包括浏览互联网、收发电子邮件等。 10. 断开连接:当用户关闭设备的WiFi开关或者设备离开WiFi网络的范围时,设备会与WiFi网络断开连接。 总的来说,AndroidWiFi连接流程图涵盖了搜索、选择、连接认证、IP地址获取、数据传输等多个环节,确保设备可以顺利地连接和使用WiFi网络。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值