android wifi详解

来源:互联网 发布:java生日提醒功能 编辑:程序博客网 时间:2024/06/07 14:52

1. 信号强度算法

    WifiManager.Java

[cpp] view plain copy
  1. /** Anything worse than or equal to this will show 0 bars. */  
  2. private static final int MIN_RSSI = -100;  
  3.   
  4. /** Anything better than or equal to this will show the max bars. */  
  5. private static final int MAX_RSSI = -55;  
  6.   
  7. /** 
  8.  * Calculates the level of the signal. This should be used any time a signal 
  9.  * is being shown. 
  10.  * 
  11.  * @param rssi The power of the signal measured in RSSI. 
  12.  * @param numLevels The number of levels to consider in the calculated 
  13.  *            level. 
  14.  * @return A level of the signal, given in the range of 0 to numLevels-1 
  15.  *         (both inclusive). 
  16.  */  
  17. public static int calculateSignalLevel(int rssi, int numLevels) {  
  18.     /* in general, numLevels is 4  */  
  19.     if (rssi <= MIN_RSSI) {  
  20.         return 0;  
  21.     } else if (rssi >= MAX_RSSI) {  
  22.         return numLevels - 1;  
  23.     } else {  
  24.         float inputRange = (MAX_RSSI - MIN_RSSI);  
  25.         float outputRange = (numLevels - 1);  
  26.           
  27.         return (int)((float)(rssi - MIN_RSSI) * outputRange / inputRange);  
  28.     }  
  29. }  

 

2. WiFi Command流程

 

3. wpa_supplicant启动流程

4. WifiService启动流程

5. SIGNAL_POLL调用流程

[cpp] view plain copy
  1. eloop_run->..  
  2. wpa_supplicant_ctrl_iface_receive-> //接收并处理来自framework的command  
  3. wpa_supplicant_ctrl_iface_process-> (SIGNAL_POLL)  
  4. wpa_supplicant_signal_poll->  
  5. wpa_drv_signal_poll  (struct wpa_supplicant *wpa_s,struct wpa_signal_info *si)->  
  6. wpa_driver_signal_poll  (void *priv, struct wpa_signal_info *si)->  
  7.     wpa_driver_wext_driver_cmd(priv, RSSI_CMD, buf, sizeof(buf))或  //driver_cmd_wext.c  
  8.     wpa_driver_wext_driver_cmd(priv, LINKSPEED_CMD, buf, sizeof(buf))->  
  9.         
  10.       struct iwreq iwr;  
  11.       iwr.u.data.pointer = buf;  
  12.       iwr.u.data.length = buf_len;  
  13.       ioctl(drv->ioctl_sock, SIOCSIWPRIV, &iwr);  
  14.       在Kernel中对应函数:  
  15.       cfg80211_wext_setpriv (wext-compat.c)  
  16.       RSSI_CMD:  
  17.       cfg80211_wireless_stats (获取当前已连接AP的信号强度等信息)  
  18.   
  19.      对于上面的LINKSPEED_CMD,如果ioctl不成功,则调用ioctl(drv->ioctl_sock, SIOCGIWRATE, &wrq)  
  20.      在Kernel中对应函数:  
  21.      cfg80211_wext_giwrate (获取当前已连接AP的发送速度)  
  22.   
  23.      //每个AP对应的信息       
  24.      struct station_info {  
  25.     u32 filled;  
  26.     u32 connected_time;  
  27.     u32 inactive_time;  
  28.     u32 rx_bytes;  
  29.     u32 tx_bytes;  
  30.     u16 llid;  
  31.     u16 plid;  
  32.     u8 plink_state;  
  33.     s8 signal;  //信号强度  
  34.     s8 signal_avg;  
  35.     struct rate_info txrate;  //发送速度  
  36.     struct rate_info rxrate;  
  37.     u32 rx_packets;  
  38.     u32 tx_packets;  
  39.     u32 tx_retries;  
  40.     u32 tx_failed;  
  41.     u32 rx_dropped_misc;  
  42.     struct sta_bss_parameters bss_param;  
  43.   
  44.     int generation;  
  45.     };  


 

接下来介绍网络可用性对评分的影响。

        该影响主要体现在,当一个网络连接建立时,系统将用该连接Ping一个Google的网站来判断该连接是否真的可以上网,如果不可以,那么就会扣掉该网络40分,从而可能导致该网络的评分低于其他网络评分,下面来看详细过程。


一、NetworkMonitor来源


        在前面《网络连接评分机制之NetworkAgent》我们分析过,当某个NetworkFactory连接上网络时,就会创建NetworkAgent对象,然后注册到ConnectivityService,而在注册过程中,ConnectivityService将会利用NetworkAgent传递过来的NetworkInfo、Messenger、分值等信息创建NetworkAgentInfo对象。而在该对象的创建过程中,将会创建一个网络监听器NetworkMonitor,下面来看这个过程:
[java] view plain copy
  1. @ConnectivityService.java  
  2. public void registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo, LinkProperties linkProperties, NetworkCapabilities networkCapabilities, int currentScore, NetworkMisc networkMisc) {  
  3.     //注册NetworkAgent时需要创建NetworkAgentInfo  
  4.     NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),  
  5.             new NetworkInfo(networkInfo), new LinkProperties(linkProperties),  
  6.             new NetworkCapabilities(networkCapabilities), currentScore, mContext, mTrackerHandler,  
  7.             new NetworkMisc(networkMisc));  
  8.     synchronized (this) {  
  9.         nai.networkMonitor.systemReady = mSystemReady;  
  10.     }  
  11.     mHandler.sendMessage(mHandler.obtainMessage(EVENT_REGISTER_NETWORK_AGENT, nai));  
  12. }  
        这就是我们所说的注册NetworkAgent时所创建的NetworkAgentInfo对象,然后来看该对象的属性:
[java] view plain copy
  1. public class NetworkAgentInfo {}  
        再来看他提供的方法:
[java] view plain copy
  1. public void addRequest(NetworkRequest networkRequest) {}  
  2. public int getCurrentScore() {}  
  3. public void setCurrentScore(int newScore) {}  
  4. public String toString() {}  
  5. public String name() {}  
        然后来看NetworkAgentInfo创建过程:
[java] view plain copy
  1. @NetworkAgentInfo.java  
  2. public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, NetworkInfo info, LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler, NetworkMisc misc) {  
  3.     //各种赋值  
  4.     this.messenger = messenger;  
  5.     asyncChannel = ac;  
  6.     network = null;  
  7.     networkInfo = info;  
  8.     linkProperties = lp;  
  9.     networkCapabilities = nc;  
  10.     currentScore = score;  
  11.     //创建NetworkMonitor  
  12.     networkMonitor = new NetworkMonitor(context, handler, this);  
  13.     networkMisc = misc;  
  14.     created = false;  
  15.     validated = false;  
  16. }  
        从这些信息我们看到,NetworkAgentInfo没有继承其他类,同时也只是提供了一些设置或者查询当前对象属性的一些方法,该对象的主要作用也就是保存各个向ConnectivityService注册的NetworkAgent,以便于查询或修改某个NetworkAgent对象的相关信息

        但是从NetworkAgentInfo的构造方法中我们看到他创建了一个NetworkMonitor对象,那么该对象的作用是什么呢?


二、NetworkMonitor作用与初始化流程


        NetworkMonitor是ConnectivityService用于管理网络连接状态而创建的状态机,主要作用就是检测当前网络的有效性。
        下面我们来看NetworkMonitor的属性与初始化流程:
[java] view plain copy
  1. @NetworkMonitor.java  
  2. public class NetworkMonitor extends StateMachine {}  
  3. public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo) {  
  4.     super(TAG + networkAgentInfo.name());  
  5.     //初始化各种成员变量  
  6.     mContext = context;  
  7.     mConnectivityServiceHandler = handler;  
  8.     mNetworkAgentInfo = networkAgentInfo;  
  9.     mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);  
  10.     mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);  
  11.     mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);  
  12.   
  13.   
  14.     //初始化状态机  
  15.     addState(mDefaultState);  
  16.     addState(mOfflineState, mDefaultState);  
  17.     addState(mValidatedState, mDefaultState);  
  18.     addState(mEvaluatingState, mDefaultState);  
  19.     addState(mUserPromptedState, mDefaultState);  
  20.     addState(mCaptivePortalState, mDefaultState);  
  21.     addState(mLingeringState, mDefaultState);  
  22.     //初始状态机为DefaultState  
  23.     setInitialState(mDefaultState);  
  24.   
  25.   
  26.     mServer = Settings.Global.getString(mContext.getContentResolver(), Settings.Global.CAPTIVE_PORTAL_SERVER);  
  27.     if (mServer == null) mServer = DEFAULT_SERVER;  
  28.   
  29.   
  30.     mLingerDelayMs = SystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);  
  31.     mReevaluateDelayMs = SystemProperties.getInt(REEVALUATE_DELAY_PROPERTY, DEFAULT_REEVALUATE_DELAY_MS);  
  32.     mIsCaptivePortalCheckEnabled = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.CAPTIVE_PORTAL_DETECTION_ENABLED, 1) == 1;  
  33.     //开始状态机  
  34.     start();  
  35. }  

        从这个状态机的初始化流程中我们可以看到两个信息:1、该类内部有七个状态机;2、初始化的状态机是DefaultState。接下来分别介绍各个状态机的作用和状态机运作流程。


三、NetworkMonitor各个状态机作用


        NetworkMonitor共有七个状态机,分别是:DefaultState、OfflineState、ValidatedState、EvaluatingState、UserPromptedState、CaptivePortalState、LingeringState。他们的作用是:
        DefaultState
            ----这是默认的状态机,也是其他所有状态机的父状态,主要处理网络连接的主要状态变化,包括连接、断开、测试、延时等模式。
        EvaluatingState
            ----验证状态,网络连上时,将会进入该状态,然后会Ping网络,判断当前网络有效性,并决定下一步是进入ValidatedState还是OfflineState或者UserPromptedState。
        OfflineState
            ----脱机状态,当Ping网络时,始终没有任何回应时,就会进入该状态。
        ValidatedState
            ----验证通过的状态,当Ping通网络时,说明当前的网络是通畅的,将会进入该状态。
        UserPromptedState
            ----验证失败状态,当Ping网络时,网络给出了重定向异常,比如接入中国移动时会跳入移动的帐号认证页面,需要用户进行网络登录后才可以继续上网。此时一般需要在界面上提示用户。
        CaptivePortalState

            ----当网络被测试失败时进入UserPromptedState后,用户可以通过发送ACTION_SIGN_IN_REQUESTED的消息来进入CaptivePortalState状态,该状态中将会监听ACTION_CAPTIVE_PORTAL_LOGGED_IN消息,并可直接由该消息指定进入ValidatedState或者OfflineState模式。


四、NetworkMonitor对网络评分的影响


        下面我们从一次正常网络接入过程来看NetworkMonitor对评分的影响。
        从NetworkMonitor初始化过程我们知道,该状态机的默认状态是DefaultState,并且从NetworkAgent向ConnectivityService注册过程可以看到,在NetworkAgent注册过程中最后将会通过updateNetworkInfo方法更新当前网络状态为CONNECTED:
[java] view plain copy
  1. @ConnectivityService.java  
  2. private void updateNetworkInfo(NetworkAgentInfo networkAgent, NetworkInfo newInfo) {  
  3.     if (state == NetworkInfo.State.CONNECTED && !networkAgent.created) {  
  4.         networkAgent.created = true;  
  5.         updateLinkProperties(networkAgent, null);  
  6.         notifyNetworkCallbacks(networkAgent, ConnectivityManager.CALLBACK_PRECHECK);  
  7.         networkAgent.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_CONNECTED);  
  8.         // Consider network even though it is not yet validated.  
  9.         rematchNetworkAndRequests(networkAgent, false);  
  10.     } else if (state == NetworkInfo.State.DISCONNECTED || state == NetworkInfo.State.SUSPENDED) {  
  11.     }  
  12. }  
        这里我们发现,更新状态的同时,向NetworkMonitor发送了CMD_NETWORK_CONNECTED的消息,根据NetworkMonitor的状态机原理,此消息将在NetworkMonitor中的DefaultState状态机中来处理:
[java] view plain copy
  1. @NetworkMonitor.java  
  2. private class DefaultState extends State {  
  3.     public boolean processMessage(Message message) {  
  4.         switch (message.what) {  
  5.             case CMD_NETWORK_CONNECTED:  
  6.                 transitionTo(mEvaluatingState);  
  7.                 return HANDLED;  
  8.         }  
  9.     }  
  10. }  
        从这里我们看到,此时的NetworkMonitor将会进入EvaluatingState的状态中,然后我们来看该状态的初始化过程:
[java] view plain copy
  1. private class EvaluatingState extends State {  
  2.     private int mRetries;  
  3.     public void enter() {  
  4.         mRetries = 0;  
  5.         sendMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);  
  6.         if (mUidResponsibleForReeval != INVALID_UID) {  
  7.             TrafficStats.setThreadStatsUid(mUidResponsibleForReeval);  
  8.             mUidResponsibleForReeval = INVALID_UID;  
  9.         }  
  10.     }  
  11.     public boolean processMessage(Message message) {  
  12.         switch (message.what) {  
  13.             case CMD_REEVALUATE:  
  14.                 if (message.arg1 != mReevaluateToken)  
  15.                     return HANDLED;  
  16.                 if (mNetworkAgentInfo.isVPN()) {  
  17.                     transitionTo(mValidatedState);  
  18.                     return HANDLED;  
  19.                 }  
  20.                 //Ping网络  
  21.                 int httpResponseCode = isCaptivePortal();  
  22.                 //根据网络回应来进入不同状态  
  23.                 if (httpResponseCode == 204) {  
  24.                     transitionTo(mValidatedState);  
  25.                 } else if (httpResponseCode >= 200 && httpResponseCode <= 399) {  
  26.                     transitionTo(mUserPromptedState);  
  27.                 } else if (++mRetries > MAX_RETRIES) {  
  28.                     transitionTo(mOfflineState);  
  29.                 } else if (mReevaluateDelayMs >= 0) {  
  30.                     Message msg = obtainMessage(CMD_REEVALUATE, ++mReevaluateToken, 0);  
  31.                     sendMessageDelayed(msg, mReevaluateDelayMs);  
  32.                 }  
  33.                 return HANDLED;  
  34.             case CMD_FORCE_REEVALUATION:  
  35.                 return HANDLED;  
  36.             default:  
  37.                 return NOT_HANDLED;  
  38.         }  
  39.     }  
  40. }  
        从这个状态机的初始化过程我们发现,进入该状态时,将会发送一个CMD_REEVALUATE的消息,然后在该状态机内部收到该消息时,就会通过isCaptivePortal方法来Ping网络:
[java] view plain copy
  1. private int isCaptivePortal() {  
  2.     if (!mIsCaptivePortalCheckEnabled) return 204;  
  3.   
  4.     HttpURLConnection urlConnection = null;  
  5.     int httpResponseCode = 599;  
  6.     try {  
  7.         //准备连接的uri  
  8.         URL url = new URL("http", mServer, "/generate_204");  
  9.         urlConnection = (HttpURLConnection) mNetworkAgentInfo.network.openConnection(url);  
  10.         urlConnection.setInstanceFollowRedirects(false);  
  11.         urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);  
  12.         urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);  
  13.         urlConnection.setUseCaches(false);  
  14.         //发起连接  
  15.         long requestTimestamp = SystemClock.elapsedRealtime();  
  16.         urlConnection.getInputStream();  
  17.         long responseTimestamp = SystemClock.elapsedRealtime();  
  18.         //获取服务器回应  
  19.         httpResponseCode = urlConnection.getResponseCode();  
  20.         //拿到回应  
  21.         if (httpResponseCode == 200 && urlConnection.getContentLength() == 0) {  
  22.             httpResponseCode = 204;  
  23.         }  
  24.         sendNetworkConditionsBroadcast(true /* response received */, httpResponseCode == 204, requestTimestamp, responseTimestamp);  
  25.     } catch (IOException e) {  
  26.         if (httpResponseCode == 599) {  
  27.         }  
  28.     } finally {  
  29.         if (urlConnection != null) {  
  30.             urlConnection.disconnect();  
  31.         }  
  32.     }  
  33.     return httpResponseCode;  
  34. }  
        从这里我们看到,其向一个url网址进行Ping操作,而这个url的具体内容为:
[java] view plain copy
  1. URL url = new URL("http", mServer, "/generate_204");  
        而这里的mServer的默认值是从NetworkMonitor初始化时获取的:
[java] view plain copy
  1. public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo) {  
  2.     mServer = Settings.Global.getString(mContext.getContentResolver(), Settings.Global.CAPTIVE_PORTAL_SERVER);  
  3.     if (mServer == null) mServer = DEFAULT_SERVER;  
  4. }  
        而DEFAULT_SERVER内容为:
[java] view plain copy
  1. private static final String DEFAULT_SERVER = "clients3.google.com";  
        这就说明,这个url的具体地址为:“http://clients3.google.com/generate_204”,然后当isCaptivePortal结束之后,EvaluatingState就会对结果进行分类:
        如果httpResponseCode=204,就说明当前网络是可用的,将会进入ValidatedState状态。
        如果httpResponseCode >= 200同时httpResponseCode <= 399,说明当前网络需要进行重定向,当认证完成后才可以有效访问网络。
        如果没有任何回应,将会通过sendMessageDelayed方式延时mReevaluateDelayMs(默认为5秒)之后再次发送CMD_REEVALUATE消息,触发循环Ping过程,直到尝试次数超过MAX_RETRIES(10次)之后,进入OfflineState状态,表示当前网络无法使用。
        那么当当前网络可用或者不可用时,对NetworkAgent评分有什么影响呢?
        我们来看当该Ping通过后,进入ValidatedState状态时将会触发什么动作:
[java] view plain copy
  1. private class ValidatedState extends State {  
  2.     @Override  
  3.     public void enter() {  
  4.         mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_VALID, 0, mNetworkAgentInfo));  
  5.     }  
  6.     @Override  
  7.     public boolean processMessage(Message message) {  
  8.         switch (message.what) {  
  9.             case CMD_NETWORK_CONNECTED:  
  10.                 transitionTo(mValidatedState);  
  11.                 return HANDLED;  
  12.             default:  
  13.                 return NOT_HANDLED;  
  14.         }  
  15.     }  
  16. }  
        这个状态机比较简单,在进入该状态机时,只是向ConnectivityService发送了一条EVENT_NETWORK_TESTED的消息,并携带了当前网络的NetworkAgentInfo对象和测试通过(NETWORK_TEST_RESULT_VALID)的参数。
        然后我们来看,当Ping不通过时,发生重定向时进入的UserPromptedState状态机会是怎样的处理:
[java] view plain copy
  1. private class UserPromptedState extends State {  
  2.     private static final String ACTION_SIGN_IN_REQUESTED = "android.net.netmon.sign_in_requested";  
  3.     private CustomIntentReceiver mUserRespondedBroadcastReceiver;  
  4.     @Override  
  5.     public void enter() {  
  6.         mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED, NETWORK_TEST_RESULT_INVALID, 0, mNetworkAgentInfo));  
  7.         mUserRespondedBroadcastReceiver = new CustomIntentReceiver(ACTION_SIGN_IN_REQUESTED, ++mUserPromptedToken, CMD_USER_WANTS_SIGN_IN);  
  8.         Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 1,  
  9.                 mNetworkAgentInfo.network.netId,  
  10.                 mUserRespondedBroadcastReceiver.getPendingIntent());  
  11.         mConnectivityServiceHandler.sendMessage(message);  
  12.     }  
  13.   
  14.   
  15.     @Override  
  16.     public boolean processMessage(Message message) {  
  17.         switch (message.what) {  
  18.             case CMD_USER_WANTS_SIGN_IN:  
  19.                 if (message.arg1 != mUserPromptedToken)  
  20.                     return HANDLED;  
  21.                 transitionTo(mCaptivePortalState);  
  22.                 return HANDLED;  
  23.             default:  
  24.                 return NOT_HANDLED;  
  25.         }  
  26.     }  
  27. }  
        这个状态机与ValidatedState类似,也是在进入时向ConnectivityService发送了同样的EVENT_NETWORK_TESTED消息,并携带当前的NetworkAgentInfo对象,不同的是,该消息中携带了一个测试不通过的参数(NETWORK_TEST_RESULT_INVALID)。
        下面我们来看ConnectivityService接收到该消息后的处理:
[java] view plain copy
  1. @ConnectivityService.java  
  2. private class NetworkStateTrackerHandler extends Handler {  
  3.     public void handleMessage(Message msg) {  
  4.         NetworkInfo info;  
  5.         switch (msg.what) {  
  6.             case NetworkMonitor.EVENT_NETWORK_TESTED: {  
  7.               NetworkAgentInfo nai = (NetworkAgentInfo)msg.obj;  
  8.               if (isLiveNetworkAgent(nai, "EVENT_NETWORK_VALIDATED")) {  
  9.                   boolean valid = (msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);  
  10.                   if (valid) {  
  11.                       //验证通过  
  12.                       final boolean previouslyValidated = nai.validated;  
  13.                       final int previousScore = nai.getCurrentScore();  
  14.                       //标记NetworkAgentInfo的validated状态为true  
  15.                       nai.validated = true;  
  16.                       rematchNetworkAndRequests(nai, !previouslyValidated);  
  17.                       if (nai.getCurrentScore() != previousScore) {  
  18.                           //将最新分数更新到其他NetworkFactory  
  19.                           sendUpdatedScoreToFactories(nai);  
  20.                       }  
  21.                   }  
  22.                   updateInetCondition(nai, valid);  
  23.                   //通知NetworkAgent  
  24.                   nai.asyncChannel.sendMessage( android.net.NetworkAgent.CMD_REPORT_NETWORK_STATUS, (valid ? NetworkAgent.VALID_NETWORK : NetworkAgent.INVALID_NETWORK), 0null);  
  25.               }  
  26.               break;  
  27.           }  
  28.         }  
  29.     }  
  30. }  
        在这里我们看到,通过与不通过的差距就在于是否将NetworkAgentInfo的validated标志位设置为true
        那么为什么这个标志位会对评分产生影响呢?我们来看NetworkAgentInfo中关于该标志位的使用:
        当NetworkAgent注册到ConnectivityService时,将会创建该NetworkAgent的NetworkAgentInfo,此时该标志位的默认状态是false:
[java] view plain copy
  1. public NetworkAgentInfo(Messenger messenger, AsyncChannel ac, NetworkInfo info, LinkProperties lp, NetworkCapabilities nc, int score, Context context, Handler handler, NetworkMisc misc) {  
  2.     this.messenger = messenger;  
  3.     asyncChannel = ac;  
  4.     network = null;  
  5.     networkInfo = info;  
  6.     linkProperties = lp;  
  7.     networkCapabilities = nc;  
  8.     currentScore = score;  
  9.     networkMonitor = new NetworkMonitor(context, handler, this);  
  10.     networkMisc = misc;  
  11.     created = false;  
  12.     //默认值为false  
  13.     validated = false;  
  14. }  
        这也很容易理解,因为此时说明网络连接建立完成,但是对于ConnectivityService来说,并不能确定该网络完全可用,因此默认状态下网络都是未经检验的。
        而当Ping通过之后,说明该网络通过了可用性检测,那么此时在对该标志位赋值也是合理的。
        而该标志位直接影响到NetworkAgentInfo中该网络的分值计算:
[java] view plain copy
  1. public int getCurrentScore() {  
  2.     //默认是NetworkAgent带过来的  
  3.     int score = currentScore;  
  4.     //如果没有通过Ping的检测,那么该网络就要被扣掉UNVALIDATED_SCORE_PENALTY=40分  
  5.     if (!validated) score -= UNVALIDATED_SCORE_PENALTY;  
  6.     //有效性保护  
  7.     if (score < 0) score = 0;  
  8.     //如果该网络是用户特意指定的,分值就是EXPLICITLY_SELECTED_NETWORK_SCORE=100  
  9.     if (networkMisc.explicitlySelected) score = EXPLICITLY_SELECTED_NETWORK_SCORE;  
  10.     return score;  
  11. }  
        从这里我们终于找到了Ping网络对评分的约束:无论是通过rematchNetworkAndRequests重新匹配最佳NetworkFactory,还是通过sendUpdatedScoreToFactories广播最新网络的分值,都是需要从NetworkAgentInfo中通过getCurrentScore获取最新分值的,而如果当前网络没有经过有效性检测,那么对其他所有的NetworkFactory来说,当前的网络分值都是被扣掉40分之后的分值
        这就完全可能让原本分值高的网络由于没有通过有效性检测,而低于其他网络优先级。

        比如WIFI默认60分,而数据连接默认50分,但是如果WIFI没有通过有效性检测,那么对数据连接来说,WIFI的分数只有60-40=20分,由此就会引发WIFI连接时,数据也在连接的异常。

        这就是网络可用性对评分的影响。



用户原本在用数据上网,但是如果到了一个有WIFI的环境,并连接上了WIFI,此时用户的手机将会自动断开数据网络,这是如何做到的呢?
        当用户来到WIFI环境时,如果连上了某个WIFI,那么此时的WIFI状态机将会进入L2ConnectedState状态,然后会更新当前NetworkInfo状态为CONNECTING状态,并用该NetworkInfo创建WIFI的NetworkAgent对象,同时标明当前WIFI分值为60。即:
[java] view plain copy
  1. mNetworkAgent = new WifiNetworkAgent(getHandler().getLooper(), mContext, "WifiNetworkAgent", mNetworkInfo, mNetworkCapabilitiesFilter, mLinkProperties, 60);  
        然后在NetworkAgent初始化过程中将会向ConnectivityService注册,然后ConnectivityService就会创建WIFI的NetworkAgentInfo对象:
[java] view plain copy
  1. NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),  
  2.     new NetworkInfo(networkInfo), new LinkProperties(linkProperties),  
  3.     new NetworkCapabilities(networkCapabilities), currentScore, mContext, mTrackerHandler,  
  4.     new NetworkMisc(networkMisc));  
        然后ConnectivityService将会在handleRegisterNetworkAgent方法中更新当前WIFI的NetworkAgentInfo为CONNECTED状态,并向NetworkMonitor发送连接成功的消息,然后经过有效性检测,WIFI的分值将会保持在60分
        然后在rematchNetworkAndRequests中,将发现现有的网络请求所使用的数据流量方案的分值(50分)低于WIFI的分值,将会把最新WIFI的分值通过sendUpdatedScoreToFactories发送到各个NetworkFactory中,其中就包括数据网络:
[java] view plain copy
  1. private void sendUpdatedScoreToFactories(NetworkRequest networkRequest, int score) {  
  2.     for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {  
  3.         nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score, 0, networkRequest);  
  4.     }  
  5. }  
        此时数据网络会再次进行分值对比,并发现自己的分值的确低于目标WIFI分值
[java] view plain copy
  1. private void evalRequest(NetworkRequestInfo n) {  
  2.     if (n.requested == false && n.score < mScore &&  
  3.             n.request.networkCapabilities.satisfiedByNetworkCapabilities(  
  4.                 mCapabilityFilter) && acceptRequest(n.request, n.score)) {  
  5.         needNetworkFor(n.request, n.score);  
  6.         n.requested = true;  
  7.     } else if (n.requested == true &&  
  8.             (n.score > mScore || n.request.networkCapabilities.satisfiedByNetworkCapabilities(mCapabilityFilter) == false || acceptRequest(n.request, n.score) == false)) {  
  9.         releaseNetworkFor(n.request);  
  10.         n.requested = false;  
  11.     }  
  12. }  
        然后数据网络就进行releaseNetworkFor操作
[java] view plain copy
  1. @DcTracker.java  
  2. protected void releaseNetworkFor(NetworkRequest networkRequest) {  
  3.     ApnContext apnContext = apnContextForNetworkRequest(networkRequest);  
  4.     if (apnContext != null) apnContext.decRefCount();  
  5. }  
        而APN参数被删掉时将会触发数据业务的关闭
[java] view plain copy
  1. @ApnContext.java  
  2. public void decRefCount() {  
  3.     synchronized (mRefCountLock) {  
  4.         if (mRefCount-- == 1) {  
  5.             //关闭数据业务  
  6.             mDcTracker.setEnabled(mDcTracker.apnTypeToId(mApnType), false);  
  7.         }  
  8.     }  
  9. }  
        然后数据业务就被关闭,从而完成网络切换的任务。



WIFI衡量接收信号强度可以用直接的RF能量dBm来表示,也可以用RSSI这个相对值。得到的值是一个0到-100的区间值,是一个int型数据,其中0到-50表示信号最好,-50到-70表示信号偏差,小于-70表示最差,有可能连接不上或者掉线。

 

下图中WIFI信号强度为-65dBm,一般-60~-70算是信号很好。

 

Received signal strength indicator (RSSI)WIFI衡量接收信号强度的一个相对值。

说白了就是让大家好理解信号强度,不然都用dBm谁知道多少dBm算信号强啊。

:

RSSI = 0时,我们PC WIFI那个Bar可能信号只有一格或一格都没有,我们说信号很差,其可能对应实际能量< -90dBm

RSSI=120~127时,WIFI Bar可能信号满格,其可能对应实际能量> -60dBm等。

 

RSSI值的定义由WIFI芯片厂家自己定,如一个厂家可以定义RSSI = 80对应-65dBm;另一个可以定义RSSI = 80对应-60dBm等。 Qualcomm AtherosRSSI一般是0-127

所以,对于相同信号强度,实际的RSSI值可能不同,对于用户还是看有几格信号最直接和省事。




用户原本在用数据上网,但是如果到了一个有WIFI的环境,并连接上了WIFI,此时用户的手机将会自动断开数据网络,这是如何做到的呢?
        当用户来到WIFI环境时,如果连上了某个WIFI,那么此时的WIFI状态机将会进入L2ConnectedState状态,然后会更新当前NetworkInfo状态为CONNECTING状态,并用该NetworkInfo创建WIFI的NetworkAgent对象,同时标明当前WIFI分值为60。即:
[java] view plain copy
  1. mNetworkAgent = new WifiNetworkAgent(getHandler().getLooper(), mContext, "WifiNetworkAgent", mNetworkInfo, mNetworkCapabilitiesFilter, mLinkProperties, 60);  
        然后在NetworkAgent初始化过程中将会向ConnectivityService注册,然后ConnectivityService就会创建WIFI的NetworkAgentInfo对象:
[java] view plain copy
  1. NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),  
  2.     new NetworkInfo(networkInfo), new LinkProperties(linkProperties),  
  3.     new NetworkCapabilities(networkCapabilities), currentScore, mContext, mTrackerHandler,  
  4.     new NetworkMisc(networkMisc));  
        然后ConnectivityService将会在handleRegisterNetworkAgent方法中更新当前WIFI的NetworkAgentInfo为CONNECTED状态,并向NetworkMonitor发送连接成功的消息,然后经过有效性检测,WIFI的分值将会保持在60分
        然后在rematchNetworkAndRequests中,将发现现有的网络请求所使用的数据流量方案的分值(50分)低于WIFI的分值,将会把最新WIFI的分值通过sendUpdatedScoreToFactories发送到各个NetworkFactory中,其中就包括数据网络:
[java] view plain copy
  1. private void sendUpdatedScoreToFactories(NetworkRequest networkRequest, int score) {  
  2.     for (NetworkFactoryInfo nfi : mNetworkFactoryInfos.values()) {  
  3.         nfi.asyncChannel.sendMessage(android.net.NetworkFactory.CMD_REQUEST_NETWORK, score, 0, networkRequest);  
  4.     }  
  5. }  
        此时数据网络会再次进行分值对比,并发现自己的分值的确低于目标WIFI分值
[java] view plain copy
  1. private void evalRequest(NetworkRequestInfo n) {  
  2.     if (n.requested == false && n.score < mScore &&  
  3.             n.request.networkCapabilities.satisfiedByNetworkCapabilities(  
  4.                 mCapabilityFilter) && acceptRequest(n.request, n.score)) {  
  5.         needNetworkFor(n.request, n.score);  
  6.         n.requested = true;  
  7.     } else if (n.requested == true &&  
  8.             (n.score > mScore || n.request.networkCapabilities.satisfiedByNetworkCapabilities(mCapabilityFilter) == false || acceptRequest(n.request, n.score) == false)) {  
  9.         releaseNetworkFor(n.request);  
  10.         n.requested = false;  
  11.     }  
  12. }  
        然后数据网络就进行releaseNetworkFor操作
[java] view plain copy
  1. @DcTracker.java  
  2. protected void releaseNetworkFor(NetworkRequest networkRequest) {  
  3.     ApnContext apnContext = apnContextForNetworkRequest(networkRequest);  
  4.     if (apnContext != null) apnContext.decRefCount();  
  5. }  
        而APN参数被删掉时将会触发数据业务的关闭
[java] view plain copy
  1. @ApnContext.java  
  2. public void decRefCount() {  
  3.     synchronized (mRefCountLock) {  
  4.         if (mRefCount-- == 1) {  
  5.             //关闭数据业务  
  6.             mDcTracker.setEnabled(mDcTracker.apnTypeToId(mApnType), false);  
  7.         }  
  8.     }  
  9. }  
        然后数据业务就被关闭,从而完成网络切换的任务。
原创粉丝点击