精华内容
下载资源
问答
  • wifi连接流程

    千次阅读 2016-11-27 22:05:12
    在平时的android开发中,经常会用到wifi相关操作,其实就应用而言,系统都是通过WifiManager对应的api来进行对应的操作 我们可以从源码的frameworks/base/api目录中看到当前系统提供的所有apipublic class WifiManager...

    在平时的android开发中,经常会用到wifi相关操作,其实就应用而言,系统都是通过WifiManager对应的api来进行对应的操作
    我们可以从源码的frameworks/base/api目录中看到当前系统提供的所有api

    public class WifiManager {
        method public int addNetwork(android.net.wifi.WifiConfiguration);
        method public static int calculateSignalLevel(int, int);
        method public void cancelWps(android.net.wifi.WifiManager.WpsCallback);
        method public static int compareSignalLevel(int, int);
        method public android.net.wifi.WifiManager.MulticastLock createMulticastLock(java.lang.String);
        method public android.net.wifi.WifiManager.WifiLock createWifiLock(int, java.lang.String);
        method public android.net.wifi.WifiManager.WifiLock createWifiLock(java.lang.String);
        method public boolean disableNetwork(int);
        method public boolean disconnect();
        method public boolean enableNetwork(int, boolean);
        method public java.util.List<android.net.wifi.WifiConfiguration> getConfiguredNetworks();
        method public android.net.wifi.WifiInfo getConnectionInfo();
        method public android.net.DhcpInfo getDhcpInfo();
        method public java.util.List<android.net.wifi.ScanResult> getScanResults();
        method public int getWifiState();
        method public boolean is5GHzBandSupported();
        method public boolean isDeviceToApRttSupported();
        method public boolean isEnhancedPowerReportingSupported();
        method public boolean isP2pSupported();
        method public boolean isPreferredNetworkOffloadSupported();
        method public boolean isScanAlwaysAvailable();
        method public boolean isTdlsSupported();
        method public boolean isWifiEnabled();
        method public boolean pingSupplicant();
        method public boolean reassociate();
        method public boolean reconnect();
        method public boolean removeNetwork(int);
        method public boolean saveConfiguration();
        method public void setTdlsEnabled(java.net.InetAddress, boolean);
        method public void setTdlsEnabledWithMacAddress(java.lang.String, boolean);
        method public boolean setWifiEnabled(boolean);
        method public boolean startScan();
        method public void startWps(android.net.wifi.WpsInfo, android.net.wifi.WifiManager.WpsCallback);
        method public int updateNetwork(android.net.wifi.WifiConfiguration);
        field public static final java.lang.String ACTION_PICK_WIFI_NETWORK = "android.net.wifi.PICK_WIFI_NETWORK";
        field public static final java.lang.String ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE = "android.net.wifi.action.REQUEST_SCAN_ALWAYS_AVAILABLE";
        field public static final int ERROR_AUTHENTICATING = 1; // 0x1
        field public static final java.lang.String EXTRA_BSSID = "bssid";
        field public static final java.lang.String EXTRA_NETWORK_INFO = "networkInfo";
        field public static final java.lang.String EXTRA_NEW_RSSI = "newRssi";
        field public static final java.lang.String EXTRA_NEW_STATE = "newState";
        field public static final java.lang.String EXTRA_PREVIOUS_WIFI_STATE = "previous_wifi_state";
        field public static final java.lang.String EXTRA_RESULTS_UPDATED = "resultsUpdated";
        field public static final java.lang.String EXTRA_SUPPLICANT_CONNECTED = "connected";
        field public static final java.lang.String EXTRA_SUPPLICANT_ERROR = "supplicantError";
        field public static final java.lang.String EXTRA_WIFI_INFO = "wifiInfo";
        field public static final java.lang.String EXTRA_WIFI_STATE = "wifi_state";
        field public static final java.lang.String NETWORK_IDS_CHANGED_ACTION = "android.net.wifi.NETWORK_IDS_CHANGED";
        field public static final java.lang.String NETWORK_STATE_CHANGED_ACTION = "android.net.wifi.STATE_CHANGE";
        field public static final java.lang.String RSSI_CHANGED_ACTION = "android.net.wifi.RSSI_CHANGED";
        field public static final java.lang.String SCAN_RESULTS_AVAILABLE_ACTION = "android.net.wifi.SCAN_RESULTS";
        field public static final java.lang.String SUPPLICANT_CONNECTION_CHANGE_ACTION = "android.net.wifi.supplicant.CONNECTION_CHANGE";
        field public static final java.lang.String SUPPLICANT_STATE_CHANGED_ACTION = "android.net.wifi.supplicant.STATE_CHANGE";
        field public static final int WIFI_MODE_FULL = 1; // 0x1
        field public static final int WIFI_MODE_FULL_HIGH_PERF = 3; // 0x3
        field public static final int WIFI_MODE_SCAN_ONLY = 2; // 0x2
        field public static final java.lang.String WIFI_STATE_CHANGED_ACTION = "android.net.wifi.WIFI_STATE_CHANGED";
        field public static final int WIFI_STATE_DISABLED = 1; // 0x1
        field public static final int WIFI_STATE_DISABLING = 0; // 0x0
        field public static final int WIFI_STATE_ENABLED = 3; // 0x3
        field public static final int WIFI_STATE_ENABLING = 2; // 0x2
        field public static final int WIFI_STATE_UNKNOWN = 4; // 0x4
        field public static final int WPS_AUTH_FAILURE = 6; // 0x6
        field public static final int WPS_OVERLAP_ERROR = 3; // 0x3
        field public static final int WPS_TIMED_OUT = 7; // 0x7
        field public static final int WPS_TKIP_ONLY_PROHIBITED = 5; // 0x5
        field public static final int WPS_WEP_PROHIBITED = 4; // 0x4
      }
    

    WifiManager和WifiServiceImpl远程通信

    其实关于WifiManager操作基本都是通过远程服务端实现,这一点稍后讨论,WifiManager中的常用方法:

    // 连接到指定网络
    public void connect(int networkId, ActionListener listener) {
            if (networkId < 0) throw new IllegalArgumentException("Network id cannot be negative");
            validateChannel();
            sAsyncChannel.sendMessage(CONNECT_NETWORK, networkId, putListener(listener));
    }
    
    // 保存网络
    public void save(WifiConfiguration config, ActionListener listener) {
            if (config == null) throw new IllegalArgumentException("config cannot be null");
            validateChannel();
            sAsyncChannel.sendMessage(SAVE_NETWORK, 0, putListener(listener), config);
    }
    
    // 忘记网络
    public void forget(int netId, ActionListener listener) {
            if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative");
            validateChannel();
            sAsyncChannel.sendMessage(FORGET_NETWORK, netId, putListener(listener));
    }

    可以面这种操作网络的实际上都是通过sAsyncChannel中进一步实现的,sAsyncChannel是一个AsyncChannel类型,其实AsyncChannel是连接WifiManager和WifiServiceImpl的桥梁

    AsyncChannel

    在WifiManager构造方法中,调用了init()方法来初始化一些参数

    private void init() {
            synchronized (sThreadRefLock) {
                if (++sThreadRefCount == 1) {
                    // 获取服务端(WifiServiceImpl)中对应的mClientHandler,WifiServiceImpl继承自IWifiManager.Stub
                    Messenger messenger = getWifiServiceMessenger();
                    if (messenger == null) {
                        sAsyncChannel = null;
                        return;
                    }
    
                    sHandlerThread = new HandlerThread("WifiManager");
                    sAsyncChannel = new AsyncChannel();
                    // CountDownLatch是一个同步辅助类,犹如倒计时计数器,创建对象时通过构造方法设置初始值,调用CountDownLatch对象的await()方法则处于等待状态,调用countDown()方法就将计数器减1,当计数到达0时,则所有等待者或单个等待者开始执行。
                    sConnected = new CountDownLatch(1);
    
                    sHandlerThread.start();
                    Handler handler = new ServiceHandler(sHandlerThread.getLooper());
                    // 1. 调用连接AsyncChannel.connect进行半连接
                    sAsyncChannel.connect(mContext, handler, messenger);
                    try {
                        sConnected.await();
                    } catch (InterruptedException e) {
                        Log.e(TAG, "interrupted wait at init");
                    }
                }
            }
    }

    上面代码主要做了两件事:
    1.通过getWifiServiceMessenger()获取WifiServiceImpl中对应的mClientHandler
    2.调用连接AsyncChannel.connect进行半连接
    为什么第二步是半连接?分析代码吧

    我们看下AsyncChannel#connect方法

    public class AsyncChannel {
        ....
    public void connect(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
            // 连接srcHandler和dstMessenger
            connected(srcContext, srcHandler, dstMessenger);
            // 在replyHalfConnected中会发送CMD_CHANNEL_HALF_CONNECTED到srcHandler处理
            replyHalfConnected(STATUS_SUCCESSFUL);
    }
    
    public void connected(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
            if (DBG) log("connected srcHandler to the dstMessenger  E");
    
            // Initialize source fields
            mSrcContext = srcContext;
            mSrcHandler = srcHandler;
            mSrcMessenger = new Messenger(mSrcHandler);
    
            // 这里的dstMessenger就是传递过来的WifiServiceImpl中对应的mClientHandler
            mDstMessenger = dstMessenger;
    
            if (DBG) log("connected srcHandler to the dstMessenger X");
    }
    
    
    private void replyHalfConnected(int status) {
            Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_HALF_CONNECTED);
            msg.arg1 = status;
            msg.obj = this;
            msg.replyTo = mDstMessenger;
            // 创建一个死亡监听,当当前mDstMessenger对应的binder死亡以后,会在DeathMonitor中发送一个"STATUS_REMOTE_DISCONNECTION"消息
            if (mConnection == null) {
                mDeathMonitor = new DeathMonitor();
                try {
                    mDstMessenger.getBinder().linkToDeath(mDeathMonitor, 0);
                } catch (RemoteException e) {
                    mDeathMonitor = null;
                    // Override status to indicate failure
                    msg.arg1 = STATUS_BINDING_UNSUCCESSFUL;
                }
            }
            // 发送CMD_CHANNEL_HALF_CONNECTED消息到mSrcHandler也就是在init方法中1.中传递进来的ServiceHandler
            mSrcHandler.sendMessage(msg);
        }
        ....
    }
    

    上述mSrcHandler.sendMessage(msg);中mSrcHandler是WifiManager$ServiceHandler的一个内部类

    private static class ServiceHandler extends Handler {
            ServiceHandler(Looper looper) {
                super(looper);
            }
    
            @Override
            public void handleMessage(Message message) {
                Object listener = removeListener(message.arg2);
                switch (message.what) {
                    case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
                        if (message.arg1 == AsyncChannel) {
                            sAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION);
                        } else {
                            sAsyncChannel = null;
                        }
                        sConnected.countDown();
                        break;
               }
          }
          ....
    }

    在ServiceHandler处理” CMD_CHANNEL_HALF_CONNECTED”消息,会继续通过AsyncChannel发送一个”CMD_CHANNEL_FULL_CONNECTION”消息

    public void sendMessage(Message msg) {
            // 2.这里会向mDstMessenger对应的handler发送一条消息,然后由其处理
            msg.replyTo = mSrcMessenger;
            try {
                mDstMessenger.send(msg);
            } catch (RemoteException e) {
                replyDisconnected(STATUS_SEND_UNSUCCESSFUL);
            }
    }

    上述mDstMessenger就是在1.处传递的Messenger,也就是通过WifiServiceImpl#getWifiServiceMessenger的

    public Messenger getWifiServiceMessenger() {
            enforceAccessPermission();
            enforceChangePermission();
            return new Messenger(mClientHandler);
    }

    因此,2.处发送的”CMD_CHANNEL_FULL_CONNECTION”消息,将会交给WifiServiceImpl中的ClientHandler来处理

    看下WifiServiceImpl的内部类ClientHandler,ClientHandler#handleMessage方法

    public void handleMessage(Message msg) {
        ....
        public void handleMessage(Message msg) {
            case AsyncChannel.CMD_CHANNEL_FULL_CONNECTION: {
                        AsyncChannel ac = new AsyncChannel();
                        // 这里的msg.replyTo就是上一步的mSrcMessenger
                        ac.connect(mContext, this, msg.replyTo);
                        break;
                    }
        }
        ....
    
    }

    此时程序又走了一边AsyncChannel#connect方法

    public class AsyncChannel {
        ....
    public void connect(Context srcContext, Handler srcHandler, Messenger dstMessenger) {
            // 连接srcHandler(WifiServiceImpl$ClientHandler)和dstMessenger(对应WifiManager$ServiceHandler)
            connected(srcContext, srcHandler, dstMessenger);
            // 在replyHalfConnected中会发送CMD_CHANNEL_HALF_CONNECTED到ClientHandler处理
            replyHalfConnected(STATUS_SUCCESSFUL);
    }
    只不过此时的mSrcHandle就是ClientHandler自己,mDstMessenger对应处理的Handler是WifiManager中的ServiceHandler,在replyHalfConnected方法中
    private void replyHalfConnected(int status) {
            Message msg = mSrcHandler.obtainMessage(CMD_CHANNEL_HALF_CONNECTED);
            msg.arg1 = status;
            msg.obj = this;
            msg.replyTo = mDstMessenger;
            // 创建一个死亡监听,当当前mDstMessenger对应的binder死亡以后,会在DeathMonitor中发送一个"STATUS_REMOTE_DISCONNECTION"消息
            if (mConnection == null) {
                mDeathMonitor = new DeathMonitor();
                try {
                    mDstMessenger.getBinder().linkToDeath(mDeathMonitor, 0);
                } catch (RemoteException e) {
                    mDeathMonitor = null;
                    // Override status to indicate failure
                    msg.arg1 = STATUS_BINDING_UNSUCCESSFUL;
                }
            }
            // 发送CMD_CHANNEL_HALF_CONNECTED消息到ClientHandler
            mSrcHandler.sendMessage(msg);
        }
        ....
    }

    所以这次发出的”CMD_CHANNEL_HALF_CONNECTED”消息,交给了WifiServiceImpl$ClientHandler处理

    public void handleMessage(Message msg) {
                switch (msg.what) {
                    case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: {
                        if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
                            // 在WifiTrafficPoller中将当前客户端的所有Messenger(和WifiManager$ServiceHandler相关联的),添加到mClients对应的list集合中
                            mTrafficPoller.addClient(msg.replyTo);
                        } else {
                            Slog.e(TAG, "Client connection failure, error=" + msg.arg1);
                        }
                        break;
    }

    到此为止WifiManager和WifiServiceImpl通过AsyncChannel(实质上是通过messenger和handler)建立的连接就完成了,这里类似于TCP中的三次握手,猜测是确保建立连接万无一失

    总结一下:

    • 在WifiManager的init方法中,通过AsyncChannel的connect方法

      a. 先发送CMD_CHANNEL_HALF_CONNECTED到自己WifiManager内部类ServiceHandler的处理
      b. 在WifiManager内部类ServiceHandler处理CMD_CHANNEL_HALF_CONNECTED消息时候,发送CMD_CHANNEL_FULL_CONNECTION消息,由于上一步在AsyncChannel#connect方法中的初始化,这里会交给WifiServiceImpl内部类ClientHandler处理

    • 在WifiServiceImpl内部类ClientHandler处理”CMD_CHANNEL_FULL_CONNECTION”消息

      会再次建立连接,只不过此时的mSrcHandle就是ClientHandler自己,mDstMessenger对应处理的Handler是WifiManager中的ServiceHandler,最终会在Service服务端的WifiTrafficPoller中将当前和客户端建立的所有Messenger(和WifiManager内部类ServiceHandler相关联的),添加到mClients对应的list集合中

    如下图:
    这里写图片描述

    连接流程

    WifiManager#connect

    public void connect(WifiConfiguration config, ActionListener listener) {
            if (config == null) throw new IllegalArgumentException("config cannot be null");
            validateChannel();
            sAsyncChannel.sendMessage(CONNECT_NETWORK, WifiConfiguration.INVALID_NETWORK_ID,
                    putListener(listener), config);
    }

    这里通过sAsyncChannel发送了一个”CONNECT_NETWORK”消息到WifiServiceImpl服务端处理,在WifiServiceImpl$ClientHandler中对于CONNECT_NETWORK消息进一步交给WifiStateMachine处理

    class ConnectModeState extends State {
        public boolean processMessage(Message message) {
             case WifiManager.CONNECT_NETWORK:
                        /**
                         *  The connect message can contain a network id passed as arg1 on message or
                         * or a config passed as obj on message.
                         * For a new network, a config is passed to create and connect.
                         * For an existing network, a network id is passed
                         */
                        netId = message.arg1;
                        config = (WifiConfiguration) message.obj;
                        mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
                        boolean updatedExisting = false;
    
                        /* Save the network config */
                        if (config != null) {
                            // When connecting to an access point, WifiStateMachine wants to update the
                            // relevant config with administrative data. This update should not be
                            // considered a 'real' update, therefore lockdown by Device Owner must be
                            // disregarded.
                            if (!recordUidIfAuthorized(config, message.sendingUid,
                                    /* onlyAnnotate */ true)) {
                                replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
                                               WifiManager.NOT_AUTHORIZED);
                                break;
                            }
    
                            String configKey = config.configKey(true /* allowCached */);
                            WifiConfiguration savedConfig =
                                    mWifiConfigStore.getWifiConfiguration(configKey);
                            if (savedConfig != null) {
                                config = savedConfig;
                                logd("CONNECT_NETWORK updating existing config with id=" +
                                        config.networkId + " configKey=" + configKey);
                                config.ephemeral = false;
                                config.autoJoinStatus = WifiConfiguration.AUTO_JOIN_ENABLED;
                                updatedExisting = true;
                            }
                            // 1.保存当前网络
                            result = mWifiConfigStore.saveNetwork(config, message.sendingUid);
                            netId = result.getNetworkId();
                        }
                        config = mWifiConfigStore.getWifiConfiguration(netId);
    
                        if (config == null) {
                            logd("CONNECT_NETWORK no config for id=" + Integer.toString(netId) + " "
                                    + mSupplicantStateTracker.getSupplicantStateName() + " my state "
                                    + getCurrentState().getName());
                            replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
                                    WifiManager.ERROR);
                            break;
                        } else {
                            String wasSkipped = config.autoJoinBailedDueToLowRssi ? " skipped" : "";
                        }
    
                        autoRoamSetBSSID(netId, "any");
    
                        if (message.sendingUid == Process.WIFI_UID
                            || message.sendingUid == Process.SYSTEM_UID) {
                            clearConfigBSSID(config, "CONNECT_NETWORK");
                        }
    
                        if (deferForUserInput(message, netId, true)) {
                            break;
                        } else if (mWifiConfigStore.getWifiConfiguration(netId).userApproved ==
                                                                        WifiConfiguration.USER_BANNED) {
                            replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
                                    WifiManager.NOT_AUTHORIZED);
                            break;
                        }
    
                        mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
    
                        boolean persist =
                            mWifiConfigStore.checkConfigOverridePermission(message.sendingUid);
                        mWifiAutoJoinController.updateConfigurationHistory(netId, true, persist);
    
                        mWifiConfigStore.setLastSelectedConfiguration(netId);
    
                        didDisconnect = false;
                        if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID
                                && mLastNetworkId != netId) {
                            /** Supplicant will ignore the reconnect if we are currently associated,
                             * hence trigger a disconnect
                             */
                            didDisconnect = true;
                            mWifiNative.disconnect();
                        }
    
                        // Make sure the network is enabled, since supplicant will not reenable it
                        mWifiConfigStore.enableNetworkWithoutBroadcast(netId, false);
                        // 2.选择当前网络,通过mWifiNative.reconnect()连接
                        if (mWifiConfigStore.selectNetwork(config, /* updatePriorities = */ true,
                                message.sendingUid) && mWifiNative.reconnect()) {
                            lastConnectAttemptTimestamp = System.currentTimeMillis();
                            targetWificonfiguration = mWifiConfigStore.getWifiConfiguration(netId);
    
                            /* The state tracker handles enabling networks upon completion/failure */
                            mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK);
                            3. 回调回去WifiManager$ServiceHandler处理
                            replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
                            if (didDisconnect) {
                                /* Expect a disconnection from the old connection */
                                transitionTo(mDisconnectingState);
                            } else if (updatedExisting && getCurrentState() == mConnectedState &&
                                    getCurrentWifiConfiguration().networkId == netId) {
                                // Update the current set of network capabilities, but stay in the
                                // current state.
                                updateCapabilities(config);
                            } else {
                                /**
                                 *  Directly go to disconnected state where we
                                 * process the connection events from supplicant
                                 **/
                                transitionTo(mDisconnectedState);
                            }
                        } else {
                            loge("Failed to connect config: " + config + " netId: " + netId);
                            replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
                                    WifiManager.ERROR);
                            break;
                        }
                        break;
         }
    }

    忘记网络

    public void forget(int netId, ActionListener listener) {
            if (netId < 0) throw new IllegalArgumentException("Network id cannot be negative");
            validateChannel();
            sAsyncChannel.sendMessage(FORGET_NETWORK, netId, putListener(listener));
    }

    同样的发送消息到WifiServiceImpl服务端,在由WifiServiceImpl发送消息到WifiStateMachine

    case WifiManager.FORGET_NETWORK:
                        // Debug only, remember last configuration that was forgotten
                        WifiConfiguration toRemove
                                = mWifiConfigStore.getWifiConfiguration(message.arg1);
                        if (toRemove == null) {
                            lastForgetConfigurationAttempt = null;
                        } else {
                            lastForgetConfigurationAttempt = new WifiConfiguration(toRemove);
                        }
                        // check that the caller owns this network
                        netId = message.arg1;
    
                        if (!mWifiConfigStore.canModifyNetwork(message.sendingUid, netId,
                                /* onlyAnnotate */ false)) {
                            logw("Not authorized to forget network "
                                 + " cnid=" + netId
                                 + " uid=" + message.sendingUid);
                            replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
                                    WifiManager.NOT_AUTHORIZED);
                            break;
                        }
                        // 忘记网络
                        if (mWifiConfigStore.forgetNetwork(message.arg1)) {
                            replyToMessage(message, WifiManager.FORGET_NETWORK_SUCCEEDED);
                            broadcastWifiCredentialChanged(WifiManager.WIFI_CREDENTIAL_FORGOT,
                                    (WifiConfiguration) message.obj);
                        } else {
                            loge("Failed to forget network");
                            replyToMessage(message, WifiManager.FORGET_NETWORK_FAILED,
                                    WifiManager.ERROR);
                        }
                        break;
    

    其他的操作都是类似的,算是简单的一个记录,今天就到这里了。

    展开全文
  • WIFI连接流程

    千次阅读 2013-08-31 13:49:56
    Wifi 连接部分   当用户选择一个AP时会弹出一个AP参数配置对话框,此对话框会显示当前选择的AP信号强度,若此AP设置了密码则需要用户输入密码才能登录。WifiSettings中的 onPreferenceTreeClick会被...

    Wifi 连接部分

     

    当用户选择一个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(nulltrue);

            } 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("wpa_ctrl_recv failed: %s/n", strerror(errno));

            strncpy(buf, WPA_EVENT_TERMINATING " - recv error", buflen-1);

            buf[buflen-1] = '/0';

            return strlen(buf);

        }

        buf[nread] = '/0';

        /* LOGD("wait_for_event: result=%d nread=%d string=/"%s/"/n", result, nread, buf); */

        /* Check for EOF on the socket */

        if (result == 0 && nread == 0) {

            /* Fabricate an event to pass up */

            LOGD("Received EOF on supplicant socket/n");

            strncpy(buf, WPA_EVENT_TERMINATING " - signal 0 received", 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/

    展开全文
  • Android 8.0 wifi连接流程

    千次阅读 2018-06-17 12:29:55
    Android 8.0 wifi连接流程 8.0 wifi的连接过程如下:

    Android 8.0 wifi连接流程

    wifi整体的framework改动还是挺大的,
    WifiStateMachine就改动了,如下。后续再把各版本的变化贴出来
    这里写图片描述

    8.0 wifi的连接过程如下:
    这里写图片描述

    展开全文
  • Android -- Wifi连接流程分析

    万次阅读 2016-07-16 09:41:58
    Android -- Wifi连接流程分析 当我们在Android手机上连接一个AP时,间接调用WifiManager的connect()方法:/** * Connect to a network with the given configuration. The network also * gets added to the ...

    Android -- Wifi连接流程分析


    当我们在Android手机上通过Settings连接一个AP时,间接调用WifiManager的connect()方法:
    /**
         * Connect to a network with the given configuration. The network also
         * gets added to the supplicant configuration.
         *
         * For a new network, this function is used instead of a
         * sequence of addNetwork(), enableNetwork(), saveConfiguration() and
         * reconnect()
         *
         * @param config the set of variables that describe the configuration,
         *            contained in a {@link WifiConfiguration} object.
         * @param listener for callbacks on success or failure. Can be null.
         * @throws IllegalStateException if the WifiManager instance needs to be
         * initialized again
         *
         * @hide
         */
        public void connect(WifiConfiguration config, ActionListener listener) {
            if (config == null) throw new IllegalArgumentException("config cannot be null");
            validateChannel();
            // Use INVALID_NETWORK_ID for arg1 when passing a config object
            // arg1 is used to pass network id when the network already exists
            sAsyncChannel.sendMessage(CONNECT_NETWORK, WifiConfiguration.INVALID_NETWORK_ID,
                    putListener(listener), config);
        }
    
        /**
         * Connect to a network with the given networkId.
         *
         * This function is used instead of a enableNetwork(), saveConfiguration() and
         * reconnect()
         *
         * @param networkId the network id identifiying the network in the
         *                supplicant configuration list
         * @param listener for callbacks on success or failure. Can be null.
         * @throws IllegalStateException if the WifiManager instance needs to be
         * initialized again
         * @hide
         */
        public void connect(int networkId, ActionListener listener) {
            if (networkId < 0) throw new IllegalArgumentException("Network id cannot be negative");
            validateChannel();
            sAsyncChannel.sendMessage(CONNECT_NETWORK, networkId, putListener(listener));
        }
    connect()方法有两种形式,一种接受WifiConfiguration对象,一种接受某个AP的networkID。WifiConfiguration描述了一个Wifi连接的所有配置信息。

    WifiManager的ServiceHandler和WifiService的ClientHandler通过异步通道进行通信。所以这里通过AsyncChannel机制,向WifiServiceImpl发送CONNECT_NETWORK消息,可知在WifiServiceImpl::ClientHandler中被处理:
    /* Client commands are forwarded to state machine */
                    case WifiManager.CONNECT_NETWORK:
                    case WifiManager.SAVE_NETWORK: {
                        WifiConfiguration config = (WifiConfiguration) msg.obj;
                        int networkId = msg.arg1;
                        if (msg.what == WifiManager.SAVE_NETWORK) {
                            Slog.e("WiFiServiceImpl ", "SAVE"
                                    + " nid=" + Integer.toString(networkId)
                                    + " uid=" + msg.sendingUid
                                    + " name="
                                    + mContext.getPackageManager().getNameForUid(msg.sendingUid));
                        }
                        if (msg.what == WifiManager.CONNECT_NETWORK) {
                            Slog.e("WiFiServiceImpl ", "CONNECT "
                                    + " nid=" + Integer.toString(networkId)
                                    + " uid=" + msg.sendingUid
                                    + " name="
                                    + mContext.getPackageManager().getNameForUid(msg.sendingUid));
                        }
    
                        if (config != null && isValid(config)) {
                            if (DBG) Slog.d(TAG, "Connect with config" + config);
                            mWifiStateMachine.sendMessage(Message.obtain(msg));
                        } else if (config == null
                                && networkId != WifiConfiguration.INVALID_NETWORK_ID) {
                            if (DBG) Slog.d(TAG, "Connect with networkId" + networkId);
                            mWifiStateMachine.sendMessage(Message.obtain(msg));
                        } else {
                            Slog.e(TAG, "ClientHandler.handleMessage ignoring invalid msg=" + msg);
                            if (msg.what == WifiManager.CONNECT_NETWORK) {
                                replyFailed(msg, WifiManager.CONNECT_NETWORK_FAILED,
                                        WifiManager.INVALID_ARGS);
                            } else {
                                replyFailed(msg, WifiManager.SAVE_NETWORK_FAILED,
                                        WifiManager.INVALID_ARGS);
                            }
                        }
                        break;
                    }
    ClientHandler中并不做具体的连接动作,主要将CONNECT_NETWORK消息被转发到WifiStateMachine中,通过Wifi状态机来驱动连接和DHCP过程 。ConnectModeState处理:
                    case WifiManager.CONNECT_NETWORK:
                        /**
                         *  The connect message can contain a network id passed as arg1 on message or
                         * or a config passed as obj on message.
                         * For a new network, a config is passed to create and connect.
                         * For an existing network, a network id is passed
                         */
                        netId = message.arg1;
                        config = (WifiConfiguration) message.obj;
                        mWifiConnectionStatistics.numWifiManagerJoinAttempt++;
                        boolean updatedExisting = false;
    
                        /* Save the network config */
                        if (config != null) {
                            // When connecting to an access point, WifiStateMachine wants to update the
                            // relevant config with administrative data. This update should not be
                            // considered a 'real' update, therefore lockdown by Device Owner must be
                            // disregarded.
                            if (!recordUidIfAuthorized(config, message.sendingUid,
                                    /* onlyAnnotate */ true)) {
                                logw("Not authorized to update network "
                                     + " config=" + config.SSID
                                     + " cnid=" + config.networkId
                                     + " uid=" + message.sendingUid);
                                replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
                                               WifiManager.NOT_AUTHORIZED);
                                break;
                            }
    
                            String configKey = config.configKey(true /* allowCached */);
                            WifiConfiguration savedConfig =
                                    mWifiConfigStore.getWifiConfiguration(configKey);
                            if (savedConfig != null) {
                                // There is an existing config with this netId, but it wasn't exposed
                                // (either AUTO_JOIN_DELETED or ephemeral; see WifiConfigStore#
                                // getConfiguredNetworks). Remove those bits and update the config.
                                config = savedConfig;
                                logd("CONNECT_NETWORK updating existing config with id=" +
                                        config.networkId + " configKey=" + configKey);
                                config.ephemeral = false;
                                config.autoJoinStatus = WifiConfiguration.AUTO_JOIN_ENABLED;
                                updatedExisting = true;
                            }
    
                            result = mWifiConfigStore.saveNetwork(config, message.sendingUid);
                            netId = result.getNetworkId();
                        }
                        config = mWifiConfigStore.getWifiConfiguration(netId);
    
                        if (config == null) {
                            logd("CONNECT_NETWORK no config for id=" + Integer.toString(netId) + " "
                                    + mSupplicantStateTracker.getSupplicantStateName() + " my state "
                                    + getCurrentState().getName());
                            replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
                                    WifiManager.ERROR);
                            break;
                        } else {
                            String wasSkipped = config.autoJoinBailedDueToLowRssi ? " skipped" : "";
                            logd("CONNECT_NETWORK id=" + Integer.toString(netId)
                                    + " config=" + config.SSID
                                    + " cnid=" + config.networkId
                                    + " supstate=" + mSupplicantStateTracker.getSupplicantStateName()
                                    + " my state " + getCurrentState().getName()
                                    + " uid = " + message.sendingUid
                                    + wasSkipped);
                        }
    
                        autoRoamSetBSSID(netId, "any");
    
                        if (message.sendingUid == Process.WIFI_UID
                            || message.sendingUid == Process.SYSTEM_UID) {
                            // As a sanity measure, clear the BSSID in the supplicant network block.
                            // If system or Wifi Settings want to connect, they will not
                            // specify the BSSID.
                            // If an app however had added a BSSID to this configuration, and the BSSID
                            // was wrong, Then we would forever fail to connect until that BSSID
                            // is cleaned up.
                            clearConfigBSSID(config, "CONNECT_NETWORK");
                        }
    
                        if (deferForUserInput(message, netId, true)) {
                            break;
                        } else if (mWifiConfigStore.getWifiConfiguration(netId).userApproved ==
                                                                        WifiConfiguration.USER_BANNED) {
                            replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
                                    WifiManager.NOT_AUTHORIZED);
                            break;
                        }
    
                        mAutoRoaming = WifiAutoJoinController.AUTO_JOIN_IDLE;
    
                        /* Tell autojoin the user did try to connect to that network if from settings */
                        boolean persist =
                            mWifiConfigStore.checkConfigOverridePermission(message.sendingUid);
                        mWifiAutoJoinController.updateConfigurationHistory(netId, true, persist);
    
                        mWifiConfigStore.setLastSelectedConfiguration(netId);
    
                        didDisconnect = false;
                        if (mLastNetworkId != WifiConfiguration.INVALID_NETWORK_ID
                                && mLastNetworkId != netId) {
                            /** Supplicant will ignore the reconnect if we are currently associated,
                             * hence trigger a disconnect
                             */
                            didDisconnect = true;
                            mWifiNative.disconnect();
                        }
    
                        // Make sure the network is enabled, since supplicant will not reenable it
                        mWifiConfigStore.enableNetworkWithoutBroadcast(netId, false);
    
                        if (mWifiConfigStore.selectNetwork(config, /* updatePriorities = */ true,
                                message.sendingUid) && mWifiNative.reconnect()) {
                            lastConnectAttemptTimestamp = System.currentTimeMillis();
                            targetWificonfiguration = mWifiConfigStore.getWifiConfiguration(netId);
    
                            /* The state tracker handles enabling networks upon completion/failure */
                            mSupplicantStateTracker.sendMessage(WifiManager.CONNECT_NETWORK);
                            replyToMessage(message, WifiManager.CONNECT_NETWORK_SUCCEEDED);
                            if (didDisconnect) {
                                /* Expect a disconnection from the old connection */
                                transitionTo(mDisconnectingState);
                            } else if (updatedExisting && getCurrentState() == mConnectedState &&
                                    getCurrentWifiConfiguration().networkId == netId) {
                                // Update the current set of network capabilities, but stay in the
                                // current state.
                                updateCapabilities(config);
                            } else {
                                /**
                                 *  Directly go to disconnected state where we
                                 * process the connection events from supplicant
                                 **/
                                transitionTo(mDisconnectedState);
                            }
                        } else {
                            loge("Failed to connect config: " + config + " netId: " + netId);
                            replyToMessage(message, WifiManager.CONNECT_NETWORK_FAILED,
                                    WifiManager.ERROR);
                            break;
                        }
                        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消息:
    /**
         * Handle all supplicant events except STATE-CHANGE
         * @param event the event type
         * @param remainder the rest of the string following the
         * event name and " — "
         */
        void handleEvent(int event, String remainder) {
            if (DBG) {
                logDbg("handleEvent " + Integer.toString(event) + "  " + remainder);
            }
            switch (event) {
                case DISCONNECTED:
                    handleNetworkStateChange(NetworkInfo.DetailedState.DISCONNECTED, remainder);
                    break;
    
                case CONNECTED:
                    handleNetworkStateChange(NetworkInfo.DetailedState.CONNECTED, remainder);
                    break;
    
                case SCAN_RESULTS:
                    mStateMachine.sendMessage(SCAN_RESULTS_EVENT);
                    break;
    
                case SCAN_FAILED:
                    mStateMachine.sendMessage(SCAN_FAILED_EVENT);
                    break;
    
                case UNKNOWN:
                    if (DBG) {
                        logDbg("handleEvent unknown: " + Integer.toString(event) + "  " + remainder);
                    }
                    break;
                default:
                    break;
            }
        }
       private void handleNetworkStateChange(NetworkInfo.DetailedState newState, String data) {
            String BSSID = null;
            int networkId = -1;
            int reason = 0;
            int ind = -1;
            int local = 0;
            Matcher match;
            if (newState == NetworkInfo.DetailedState.CONNECTED) {
                match = mConnectedEventPattern.matcher(data);
                if (!match.find()) {
                   if (DBG) Log.d(TAG, "handleNetworkStateChange: Couldnt find BSSID in event string");
                } else {
                    BSSID = match.group(1);
                    try {
                        networkId = Integer.parseInt(match.group(2));
                    } catch (NumberFormatException e) {
                        networkId = -1;
                    }
                }
                notifyNetworkStateChange(newState, BSSID, networkId, reason);
            } else if (newState == NetworkInfo.DetailedState.DISCONNECTED) {
                match = mDisconnectedEventPattern.matcher(data);
                if (!match.find()) {
                   if (DBG) Log.d(TAG, "handleNetworkStateChange: Could not parse disconnect string");
                } else {
                    BSSID = match.group(1);
                    try {
                        reason = Integer.parseInt(match.group(2));
                    } catch (NumberFormatException e) {
                        reason = -1;
                    }
                    try {
                        local = Integer.parseInt(match.group(3));
                    } catch (NumberFormatException e) {
                        local = -1;
                    }
                }
                notifyNetworkStateChange(newState, BSSID, local, reason);
            }
        }
    
        /**
         * Send the state machine a notification that the state of Wifi connectivity
         * has changed.
         * @param newState the new network state
         * @param BSSID when the new state is {@link NetworkInfo.DetailedState#CONNECTED},
         * this is the MAC address of the access point. Otherwise, it
         * is {@code null}.
         * @param netId the configured network on which the state change occurred
         */
        void notifyNetworkStateChange(NetworkInfo.DetailedState newState,
                                      String BSSID, int netId, int reason) {
            if (newState == NetworkInfo.DetailedState.CONNECTED) {
                Message m = mStateMachine.obtainMessage(NETWORK_CONNECTION_EVENT,
                        netId, reason, BSSID);
                mStateMachine.sendMessage(m);
            } else {
    
                Message m = mStateMachine.obtainMessage(NETWORK_DISCONNECTION_EVENT,
                        netId, reason, BSSID);
                if (DBG) logDbg("WifiMonitor notify network disconnect: "
                        + BSSID
                        + " reason=" + Integer.toString(reason));
                mStateMachine.sendMessage(m);
            }
        }
    WifiMonitor与wpa_s之间的通信是通过socket建立的,如前几篇博客所介绍的那样:WifiMonitor通过建立socket连接与wpa_s通信;每当wpa_s有事件要上报时,WiFiMonitor会解析该event,并转发到Wifi状态机中。

    WifiMonitor接收到wpa_s的连接事件时,向WifiStateMachine发送NETWORK_CONNECTION_EVENT消息,通知状态机底层无线已经连接成功,下一步可以获取IP了。

    转到WifiStateMachine中,ConnectModeState进行处理:
    case WifiMonitor.NETWORK_CONNECTION_EVENT:
                        if (DBG) log("Network connection established");
                        mLastNetworkId = message.arg1; //成功加入到某无线网络中的AP的NetworkId
                        mLastBssid = (String) message.obj;
    
                        mWifiInfo.setBSSID(mLastBssid);
                        mWifiInfo.setNetworkId(mLastNetworkId);
    
                        sendNetworkStateChangeBroadcast(mLastBssid);
                        transitionTo(mObtainingIpState);
                        break;
    这里会将这次连接的AP的networkID保存下来,并进入到ObtainingIpState状态去真正触发DHCP动作。L2ConnectedState是ObtainingIpState的父状态,看它的enter()函数:
    public void enter() {
                mRssiPollToken++;
                if (mEnableRssiPolling) {
                    sendMessage(CMD_RSSI_POLL, mRssiPollToken, 0);
                }
                if (mNetworkAgent != null) {
                    loge("Have NetworkAgent when entering L2Connected");
                    setNetworkDetailedState(DetailedState.DISCONNECTED);
                }
                setNetworkDetailedState(DetailedState.CONNECTING);//更新当前的网络连接状态
    
                if (!TextUtils.isEmpty(mTcpBufferSizes)) {
                    mLinkProperties.setTcpBufferSizes(mTcpBufferSizes);
                }
                mNetworkAgent = new WifiNetworkAgent(getHandler().getLooper(), mContext,
                        "WifiNetworkAgent", mNetworkInfo, mNetworkCapabilitiesFilter,
                        mLinkProperties, 60);//此处创建一个NetworkAgent对象用于向ConnectivityService通知相应的网络配置更新操作
    
                // We must clear the config BSSID, as the wifi chipset may decide to roam
                // from this point on and having the BSSID specified in the network block would
                // cause the roam to faile and the device to disconnect
                clearCurrentConfigBSSID("L2ConnectedState");
    
                try {
                    mIpReachabilityMonitor = new IpReachabilityMonitor(
                            mInterfaceName,
                            new IpReachabilityMonitor.Callback() {
                                @Override
                                public void notifyLost(InetAddress ip, String logMsg) {
                                    sendMessage(CMD_IP_REACHABILITY_LOST, logMsg);
                                }
                            });
                } catch (IllegalArgumentException e) {
                    Log.wtf("Failed to create IpReachabilityMonitor", e);
                }
            }
    我们再看ObtainingIpState::enter()方法看如何获取获取IP地址:
            @Override
            public void enter() {
                if (DBG) {
                    String key = "";
                    if (getCurrentWifiConfiguration() != null) {
                        key = getCurrentWifiConfiguration().configKey();
                    }
                    log("enter ObtainingIpState netId=" + Integer.toString(mLastNetworkId)
                            + " " + key + " "
                            + " roam=" + mAutoRoaming
                            + " static=" + mWifiConfigStore.isUsingStaticIp(mLastNetworkId)
                            + " watchdog= " + obtainingIpWatchdogCount);
                }
    
                // Reset link Debouncing, indicating we have successfully re-connected to the AP
                // We might still be roaming
                linkDebouncing = false;
    
                // Send event to CM & network change broadcast
                setNetworkDetailedState(DetailedState.OBTAINING_IPADDR);
    
                // We must clear the config BSSID, as the wifi chipset may decide to roam
                // from this point on and having the BSSID specified in the network block would
                // cause the roam to faile and the device to disconnect
                clearCurrentConfigBSSID("ObtainingIpAddress");
    
                try {
                    mNwService.enableIpv6(mInterfaceName);
                } catch (RemoteException re) {
                    loge("Failed to enable IPv6: " + re);
                } catch (IllegalStateException e) {
                    loge("Failed to enable IPv6: " + e);
                }
    
                if (!mWifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
                    if (isRoaming()) {
                        renewDhcp();
                    } else {
                        // Remove any IP address on the interface in case we're switching from static
                        // IP configuration to DHCP. This is safe because if we get here when not
                        // roaming, we don't have a usable address.
                        clearIPv4Address(mInterfaceName);
                        startDhcp();// DHCP过程启动
                    }
                    obtainingIpWatchdogCount++;
                    logd("Start Dhcp Watchdog " + obtainingIpWatchdogCount);
                    // Get Link layer stats so as we get fresh tx packet counters
                    getWifiLinkLayerStats(true);
                    sendMessageDelayed(obtainMessage(CMD_OBTAINING_IP_ADDRESS_WATCHDOG_TIMER,
                            obtainingIpWatchdogCount, 0), OBTAINING_IP_ADDRESS_GUARD_TIMER_MSEC);
                } else {
                    // stop any running dhcp before assigning static IP
                    stopDhcp();
                    StaticIpConfiguration config = mWifiConfigStore.getStaticIpConfiguration(
                            mLastNetworkId);
                    if (config.ipAddress == null) {
                        logd("Static IP lacks address");
                        sendMessage(CMD_STATIC_IP_FAILURE);
                    } else {
                        InterfaceConfiguration ifcg = new InterfaceConfiguration();
                        ifcg.setLinkAddress(config.ipAddress);
                        ifcg.setInterfaceUp();
                        try {
                            mNwService.setInterfaceConfig(mInterfaceName, ifcg);
                            if (DBG) log("Static IP configuration succeeded");
                            DhcpResults dhcpResults = new DhcpResults(config);
                            sendMessage(CMD_STATIC_IP_SUCCESS, dhcpResults);
                        } catch (RemoteException re) {
                            loge("Static IP configuration failed: " + re);
                            sendMessage(CMD_STATIC_IP_FAILURE);
                        } catch (IllegalStateException e) {
                            loge("Static IP configuration failed: " + e);
                            sendMessage(CMD_STATIC_IP_FAILURE);
                        }
                    }
                }
            }
    这里Wifi分了两种连接方式,Static IP和DHCP。这里区分静态和DHCP是通过WifiConfiguration对象来处理的。WifiConfiguration代表一个配置过的AP连接,主要包含IpAssignment(标识上层的连接方式是静态还是DHCP)、AP的networkID、AP的名字等等。

    我们主要看动态获取IP的过程。进入DHCP流程之前,会先清除当前的IP地址信息。着重看startDhcp()函数处理:
        void startDhcp() {
            maybeInitDhcpStateMachine(); //确保初始化WifiStateMachine持有的mDhcpStateMachine对象,会传入当前的WifiStateMachine对象,用来回发消息
            mDhcpStateMachine.registerForPreDhcpNotification();//注册mRegisteredForPreDhcpNotification字段为true,表明我们在发送DHCP包之前需要做一些准备工作
            mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_START_DHCP);//发送开始DHCP的消息
        }
    DhcpStateMachine是一个小状态机,它主要处理DHCP下的IP地址获取过程,并将获取到的DHCP结果告知WifiStateMachine。看DhcpStateMachine处理该消息的过程:
    case CMD_START_DHCP:
                        if (mRegisteredForPreDhcpNotification) {
                            /* Notify controller before starting DHCP */
                            mController.sendMessage(CMD_PRE_DHCP_ACTION);
                            transitionTo(mWaitBeforeStartState);
                        } else {
                            if (runDhcpStart()) {
                                transitionTo(mRunningState);
                            }
                        }
                        break;
    由于我们之前设置了mRegisteredForPreDhcpNotification为true,这里会向WifiStateMachine发送CMD_PRE_DHCP_ACTION消息,告知Wifi状态机做一些DHCP之前的预处理工作。L2ConnectedState处理该消息:
    case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
                      handlePreDhcpSetup();
                      break;
        void handlePreDhcpSetup() {
            mDhcpActive = true;
            if (!mBluetoothConnectionActive) {
                /*
                 * There are problems setting the Wi-Fi driver's power
                 * mode to active when bluetooth coexistence mode is
                 * enabled or sense.
                 * <p>
                 * We set Wi-Fi to active mode when
                 * obtaining an IP address because we've found
                 * compatibility issues with some routers with low power
                 * mode.
                 * <p>
                 * In order for this active power mode to properly be set,
                 * we disable coexistence mode until we're done with
                 * obtaining an IP address.  One exception is if we
                 * are currently connected to a headset, since disabling
                 * coexistence would interrupt that connection.
                 */
                // Disable the coexistence mode
                mWifiNative.setBluetoothCoexistenceMode(
                        mWifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED);
            }
    
            // Disable power save and suspend optimizations during DHCP
            // Note: The order here is important for now. Brcm driver changes
            // power settings when we control suspend mode optimizations.
            // TODO: Remove this comment when the driver is fixed.
            setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, false);
            mWifiNative.setPowerSave(false);
    
            // Update link layer stats
            getWifiLinkLayerStats(false);
    
            /* P2p discovery breaks dhcp, shut it down in order to get through this */
            Message msg = new Message();
            msg.what = WifiP2pServiceImpl.BLOCK_DISCOVERY;
            msg.arg1 = WifiP2pServiceImpl.ENABLED;
            msg.arg2 = DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE;
            msg.obj = mDhcpStateMachine;
            mWifiP2pChannel.sendMessage(msg);
        }
    从注释可知,为了保证Wifi DHCP过程的顺利进行,对Bluetooth、Power和P2p都做了处理工作,其中:蓝牙会Disable the coexistence mode;停止p2p的discovery过程,防止影响DHCP流程。MD_PRE_DHCP_ACTION_COMPLETE消息会在WifiP2pServiceImpl设置完p2p部分后,被转发到DhcpStateMachine,告知预处理工作已经结束,可以进行DHCP了。看DhcpStateMachine中的消息处理:
    case CMD_PRE_DHCP_ACTION_COMPLETE:
                        if (runDhcpStart()) {
                            transitionTo(mRunningState);
                        } else {
                            transitionTo(mPollingState);
                        }
                        break;
        private boolean runDhcpStart() {
            /* Stop any existing DHCP daemon before starting new */
            NetworkUtils.stopDhcp(mInterfaceName);//在启动新的DHCP流程之前,停止当前正在进行的DHCP过程
            mDhcpResults = null;
    
            if (DBG) Log.d(TAG, "DHCP request on " + mInterfaceName);
            if (!NetworkUtils.startDhcp(mInterfaceName) || !dhcpSucceeded()) {
                Log.e(TAG, "DHCP request failed on " + mInterfaceName + ": " +
                        NetworkUtils.getDhcpError());
                mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_FAILURE, 0)
                        .sendToTarget();
                return false;
            }
            return true;
        }
    调用NetworkUtils.startDhcp()方法启动DHCP流程去获取IP地址,调用dhcpSucceeded()方法获取该次DHCP的DhcpResults对象,它包含了IP地址、网关、DNS等等地址信息。
    private boolean dhcpSucceeded() {
            DhcpResults dhcpResults = new DhcpResults();
            if (!NetworkUtils.getDhcpResults(mInterfaceName, dhcpResults)) {
                return false;
            }
    
            if (DBG) Log.d(TAG, "DHCP results found for " + mInterfaceName);
            long leaseDuration = dhcpResults.leaseDuration; //int to long conversion
    
            //Sanity check for renewal
            if (leaseDuration >= 0) {
                //TODO: would be good to notify the user that his network configuration is
                //bad and that the device cannot renew below MIN_RENEWAL_TIME_SECS
                if (leaseDuration < MIN_RENEWAL_TIME_SECS) {
                    leaseDuration = MIN_RENEWAL_TIME_SECS;
                }
                //Do it a bit earlier than half the lease duration time
                //to beat the native DHCP client and avoid extra packets
                //48% for one hour lease time = 29 minutes
                mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                        SystemClock.elapsedRealtime() +
                        leaseDuration * 480, //in milliseconds
                        mDhcpRenewalIntent);
            } else {
                //infinite lease time, no renewal needed
            }
    
            // Fill in any missing fields in dhcpResults from the previous results.
            // If mDhcpResults is null (i.e. this is the first server response),
            // this is a noop.
            dhcpResults.updateFromDhcpRequest(mDhcpResults);
            mDhcpResults = dhcpResults;
            mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, dhcpResults)
                .sendToTarget();
            return true;
        }
    如果getDhcpResults()函数执行成功,DhcpStateMachine就会发送CMD_POST_DHCP_ACTION消息给WifiStateMachine,状态位是DHCP_SUCCESS,并附加runDhcp()获取到的DhcpResult对象;失败时则附加DHCP_FAILURE。L2ConnectedState处理CMD_POST_DHCP_ACTION消息:
    case DhcpStateMachine.CMD_POST_DHCP_ACTION:
                      handlePostDhcpSetup();
                      if (message.arg1 == DhcpStateMachine.DHCP_SUCCESS) {
                          if (DBG) log("DHCP successful");
                          handleIPv4Success((DhcpResults) message.obj, DhcpStateMachine.DHCP_SUCCESS);
                          // We advance to mConnectedState because handleIPv4Success will call
                          // updateLinkProperties, which then sends CMD_IP_CONFIGURATION_SUCCESSFUL.
                      } else if (message.arg1 == DhcpStateMachine.DHCP_FAILURE) {
                          mWifiLogger.captureBugReportData(WifiLogger.REPORT_REASON_DHCP_FAILURE);
                          if (DBG) {
                              int count = -1;
                              WifiConfiguration config = getCurrentWifiConfiguration();
                              if (config != null) {
                                  count = config.numConnectionFailures;
                              }
                              log("DHCP failure count=" + count);
                          }
                          handleIPv4Failure(DhcpStateMachine.DHCP_FAILURE);
                          // As above, we transition to mDisconnectingState via updateLinkProperties.
                      }
                      break;
    首先调用handlePostDhcpSetup()重置之前所做的预处理动作;再处理附加的状态位,成功则调用handleIPv4Success()更新网络的配置信息:
    private void handleIPv4Success(DhcpResults dhcpResults, int reason) {
    
            if (PDBG) {
                logd("handleIPv4Success <" + dhcpResults.toString() + ">");
                logd("link address " + dhcpResults.ipAddress);
            }
    
            Inet4Address addr;
            synchronized (mDhcpResultsLock) {
                mDhcpResults = dhcpResults;//保存当前的DHCP结果
                addr = (Inet4Address) dhcpResults.ipAddress.getAddress();
            }
    
            if (isRoaming()) {
                int previousAddress = mWifiInfo.getIpAddress();
                int newAddress = NetworkUtils.inetAddressToInt(addr);
                if (previousAddress != newAddress) {
                    logd("handleIPv4Success, roaming and address changed" +
                            mWifiInfo + " got: " + addr);
                }
            }
            mWifiInfo.setInetAddress(addr);
            mWifiInfo.setMeteredHint(dhcpResults.hasMeteredHint());
            updateLinkProperties(reason);//更新网络配置信息
        }
    
    updateLinkProperties()更新当前的网络配置信息:
        private void updateLinkProperties(int reason) {
            LinkProperties newLp = makeLinkProperties();//根据DHCP的结果创建新的LinkProperties对象,用于对比DHCP前后的网络配置是否已经改变
    
            final boolean linkChanged = !newLp.equals(mLinkProperties);
            final boolean wasProvisioned = isProvisioned(mLinkProperties);
            final boolean isProvisioned = isProvisioned(newLp);
            // TODO: Teach LinkProperties how to understand static assignment
            // and simplify all this provisioning change detection logic by
            // unifying it under LinkProperties.compareProvisioning().
            final boolean lostProvisioning =
                    (wasProvisioned && !isProvisioned) ||
                    (mLinkProperties.hasIPv4Address() && !newLp.hasIPv4Address()) ||
                    (mLinkProperties.isIPv6Provisioned() && !newLp.isIPv6Provisioned());
            final DetailedState detailedState = getNetworkDetailedState();
    
            if (linkChanged) { //网络配置信息改变
                if (DBG) {
                    log("Link configuration changed for netId: " + mLastNetworkId
                            + " old: " + mLinkProperties + " new: " + newLp);
                }
                mLinkProperties = newLp;//将新的配置信息保存到该字段中
                if (mIpReachabilityMonitor != null) {
                    mIpReachabilityMonitor.updateLinkProperties(mLinkProperties);
                }
                if (mNetworkAgent != null) mNetworkAgent.sendLinkProperties(mLinkProperties);//通过NetworkAgent对象通知ConnectifyService更新网络配置信息
            }
    
            if (lostProvisioning) {
                log("Lost IP layer provisioning!" +
                        " was: " + mLinkProperties +
                        " now: " + newLp);
            }
    
            // If we just configured or lost IP configuration, do the needful.
            // We don't just call handleSuccessfulIpConfiguration() or handleIpConfigurationLost()
            // here because those should only be called if we're attempting to connect or already
            // connected, whereas updateLinkProperties can be called at any time.
            switch (reason) {
                case DhcpStateMachine.DHCP_SUCCESS:
                case CMD_STATIC_IP_SUCCESS:
                    // IPv4 provisioning succeded. Advance to connected state.
                    sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);
                    if (!isProvisioned) {
                        // Can never happen unless DHCP reports success but isProvisioned thinks the
                        // resulting configuration is invalid (e.g., no IPv4 address, or the state in
                        // mLinkProperties is out of sync with reality, or there's a bug in this code).
                        // TODO: disconnect here instead. If our configuration is not usable, there's no
                        // point in staying connected, and if mLinkProperties is out of sync with
                        // reality, that will cause problems in the future.
                        logd("IPv4 config succeeded, but not provisioned");
                    }
                    break;
    
                case DhcpStateMachine.DHCP_FAILURE:
                    // DHCP failed. If we're not already provisioned, or we had IPv4 and now lost it,
                    // give up and disconnect.
                    // If we're already provisioned (e.g., IPv6-only network), stay connected.
                    if (!isProvisioned || lostProvisioning) {
                        sendMessage(CMD_IP_CONFIGURATION_LOST);
                    } else {
                        // DHCP failed, but we're provisioned (e.g., if we're on an IPv6-only network).
                        sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);
    
                        // To be sure we don't get stuck with a non-working network if all we had is
                        // IPv4, remove the IPv4 address from the interface (since we're using DHCP,
                        // and DHCP failed). If we had an IPv4 address before, the deletion of the
                        // address  will cause a CMD_UPDATE_LINKPROPERTIES. If the IPv4 address was
                        // necessary for provisioning, its deletion will cause us to disconnect.
                        //
                        // This shouldn't be needed, because on an IPv4-only network a DHCP failure will
                        // have empty DhcpResults and thus empty LinkProperties, and isProvisioned will
                        // not return true if we're using DHCP and don't have an IPv4 default route. So
                        // for now it's only here for extra redundancy. However, it will increase
                        // robustness if we move to getting IPv4 routes from netlink as well.
                        loge("DHCP failure: provisioned, clearing IPv4 address.");
                        if (!clearIPv4Address(mInterfaceName)) {
                            sendMessage(CMD_IP_CONFIGURATION_LOST);
                        }
                    }
                    break;
    
                case CMD_STATIC_IP_FAILURE:
                    // Static configuration was invalid, or an error occurred in applying it. Give up.
                    sendMessage(CMD_IP_CONFIGURATION_LOST);
                    break;
    
                case CMD_UPDATE_LINKPROPERTIES:
                    // IP addresses, DNS servers, etc. changed. Act accordingly.
                    if (lostProvisioning) {
                        // We no longer have a usable network configuration. Disconnect.
                        sendMessage(CMD_IP_CONFIGURATION_LOST);
                    } else if (!wasProvisioned && isProvisioned) {
                        // We have a usable IPv6-only config. Advance to connected state.
                        sendMessage(CMD_IP_CONFIGURATION_SUCCESSFUL);
                    }
                    if (linkChanged && getNetworkDetailedState() == DetailedState.CONNECTED) {
                        // If anything has changed and we're already connected, send out a notification.
                        sendLinkConfigurationChangedBroadcast();
                    }
                    break;
            }
        }
    最后会根据DHCP是否成功发送CMD_IP_CONFIGURATION_SUCCESSFUL消息表明IP的配置已经完成(因为我们已经告知了ConnectifyService去更新网络配置,并附加了新的mLinkProperties对象),L2ConnectedState处理CMD_IP_CONFIGURATION_SUCCESSFUL消息:
    case CMD_IP_CONFIGURATION_SUCCESSFUL:
                        handleSuccessfulIpConfiguration();
                        sendConnectedState();
                        transitionTo(mConnectedState);
    handleSuccessfulIpConfiguration()函数处理:
    private void handleSuccessfulIpConfiguration() {
            mLastSignalLevel = -1; // Force update of signal strength
            WifiConfiguration c = getCurrentWifiConfiguration();//获取代表当前连接的网络的WifiConfiguration对象
            if (c != null) {
                // Reset IP failure tracking
                c.numConnectionFailures = 0;
    //重置numConnectionFailures字段,因为在WifiConfigStore中,会根据numConnectionFailures字段判断当前网络的连接失败次数,默认失败10次时,会终止重连操作
                // Tell the framework whether the newly connected network is trusted or untrusted.
                updateCapabilities(c);
            }
            if (c != null) {
                ScanResult result = getCurrentScanResult();
                if (result == null) {
                    logd("WifiStateMachine: handleSuccessfulIpConfiguration and no scan results" +
                            c.configKey());
                } else {
                    // Clear the per BSSID failure count
                    result.numIpConfigFailures = 0;
                    // Clear the WHOLE BSSID blacklist, which means supplicant is free to retry
                    // any BSSID, even though it may already have a non zero ip failure count,
                    // this will typically happen if the user walks away and come back to his arrea
                    // TODO: implement blacklisting based on a timer, i.e. keep BSSID blacklisted
                    // in supplicant for a couple of hours or a day
                    mWifiConfigStore.clearBssidBlacklist();
                }
            }
        }
    sendConnectedState()主要做一些网络状态的更新操作,发送NETWORK_STATE_CHANGED_ACTION通知当前的网络状态已经变化。此广播会附加WifiStateMachine中三个常用的字段值:
        /**
         * The link properties of the wifi interface.
         * Do not modify this directly; use updateLinkProperties instead.
         */
        private LinkProperties mLinkProperties; //保存当前网络连接的配置信息,包括IP地址集、DNS地址集、路由地址集等
        // NOTE: Do not return to clients - use #getWiFiInfoForUid(int)
        private WifiInfo mWifiInfo;//描述了一个Wifi连接的状态,通过该对象我们可以得知当前Wifi连接的SSID、netId、IP地址、Mac地址等信息
        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的人来说,是比较痛苦的。








    展开全文
  • Wifi连接流程分析

    千次阅读 2014-02-17 14:40:23
    Wifi 连接部分   当用户选择一个AP时会弹出一个AP参数配置对话框,此对话框会显示当前选择的AP信号强度,若此AP设置了密码则需要用户输入密码才能登录。WifiSettings中的 onPreferenceTreeClick会被调用 @...
  • wifi连接流程分析

    千次阅读 2013-02-26 19:56:57
    Wifi 连接部分 当用户选择一个AP时会弹出一个AP参数配置对话框,此对话框会显示当前选择的AP信号强度,若此AP设置了密码则需要用户输入密码才能登录。WifiSettings中的 onPreferenceTreeClick会被调用 @...
  • WIFI连接流程分析

    2012-10-12 14:11:35
    Wifi 连接部分   当用户选择一个AP时会弹出一个AP参数配置对话框,此对话框会显示当前选择的AP信号强度,若此AP设置了密码则需要用户输入密码才能登录。WifiSettings中的onPreferenceTreeClick会被调用 @...
  • (一百四十五)Android P wifi连接流程

    千次阅读 2019-06-01 19:40:08
    再回头看下连接流程 /** * Initiates connection to a network specified by the user/app. This method checks if the * requesting app holds the NETWORK_SETTINGS permission. * * @param netId Id ...
  • 前置代码梳理基础: (一百四十五)Android P wifi连接流程 ...之前在(一百四十五)Android P wifi连接流程中梳理了WiFi连接流程,WifiStateMachine中有个有意思的方法叫做 setUserConnectChoi...
  • 在开发的过程中大家一般都会选择使用数据线连接的方式进行调试,但是有些时候比如使用模拟器时就不能这样了,所以有必要来研究下怎么使用adb通过wifi连接我们的Android; 那么我们今天分别使用adb连接移动设备,...
  • Android O wifi 流程

    2020-08-19 13:28:10
    这里写目录标题一级目录二级目录三级目录1、wifi打开流程2、wifi扫描流程3、wifi扫描结果获取流程4、wifi连接流程5、wifi 热点打开流程 一级目录 二级目录 三级目录 1、wifi打开流程 2、wifi扫描流程 3、wifi扫描...
  • wifi 链接流程

    千次阅读 2015-09-15 11:16:45
    WIFI连接流程 分类:Android2011-07-2110:55268人阅读评论(0)收藏举报 Wifi 连接部分   当用户选择一个AP时会弹出一个AP参数配置对话框,此对话框会显示当前选择的AP信号强度,若此AP设置了密码则...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 18,810
精华内容 7,524
关键字:

wifi连接流程