(九十九) Android O wps 流程简单梳理

版权声明:本文为博主原创文章,未经授权禁止转载,O(∩_∩)O谢谢 https://blog.csdn.net/sinat_20059415/article/details/83036128

1.wps简介

wifi的wps连接主要分两种,一种是按钮,一种是pin码。比如说测试机使用wps连接,那么选择按钮或者pin码方式,服务端比如是路由器或者辅测机在限定时间内按下按钮或者输入测试机提供的pin码即可连接,相当于一种不需要选择ssid并简化输入密码的连接方式,保密性也比较好,毕竟不要输密码嘛。

注:本文梳理忽略WifiStateMachine以下流程。

2.流程梳理

2.1 WpsDialog

    private enum DialogState {
        WPS_INIT,
        WPS_START,
        WPS_COMPLETE,
        CONNECTED, //WPS + IP config is done
        WPS_FAILED
    }
    DialogState mDialogState = DialogState.WPS_INIT;

首先这个对话框有5种如上状态,初始状态为WPS_INIT


    public WpsDialog(Context context, int wpsSetup) {
        super(context);
        mContext = context;
        mWpsSetup = wpsSetup;

        class WpsListener extends WifiManager.WpsCallback {

            public void onStarted(String pin) {
                if (pin != null) {
                    updateDialog(DialogState.WPS_START, String.format(
                            mContext.getString(R.string.wifi_wps_onstart_pin), pin));
                } else {
                    updateDialog(DialogState.WPS_START, mContext.getString(
                            R.string.wifi_wps_onstart_pbc));
                }
            }

            public void onSucceeded() {
                updateDialog(DialogState.WPS_COMPLETE,
                        mContext.getString(R.string.wifi_wps_complete));
            }

            public void onFailed(int reason) {
                String msg;
                switch (reason) {
                    case WifiManager.WPS_OVERLAP_ERROR:
                        msg = mContext.getString(R.string.wifi_wps_failed_overlap);
                        break;
                    case WifiManager.WPS_WEP_PROHIBITED:
                        msg = mContext.getString(R.string.wifi_wps_failed_wep);
                        break;
                    case WifiManager.WPS_TKIP_ONLY_PROHIBITED:
                        msg = mContext.getString(R.string.wifi_wps_failed_tkip);
                        break;
                    case WifiManager.IN_PROGRESS:
                        msg = mContext.getString(R.string.wifi_wps_in_progress);
                        break;
                    default:
                        msg = mContext.getString(R.string.wifi_wps_failed_generic);
                        break;
                }
                updateDialog(DialogState.WPS_FAILED, msg);
            }
        }

        mWpsListener = new WpsListener();


        mFilter = new IntentFilter();
        mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
        mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
        mReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                handleEvent(context, intent);
            }
        };
        setCanceledOnTouchOutside(false);
    }

这边进行一些初始化,主要初始化了一个mWpsListener,并注册了一个广播接收器,消息处理如下。

    private void handleEvent(Context context, Intent intent) {
        String action = intent.getAction();
        if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
            final int state = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
                    WifiManager.WIFI_STATE_UNKNOWN);
            if (state == WifiManager.WIFI_STATE_DISABLED) {
                if (mTimer != null) {
                    mTimer.cancel();
                    mTimer = null;
                }
                String msg = mContext.getString(R.string.wifi_wps_failed_wifi_disconnected);
                updateDialog(DialogState.WPS_FAILED, msg);
            }
        } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
            NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(
                    WifiManager.EXTRA_NETWORK_INFO);
            final NetworkInfo.DetailedState state = info.getDetailedState();
            if (state == DetailedState.CONNECTED &&
                    mDialogState == DialogState.WPS_COMPLETE) {
                WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
                if (wifiInfo != null) {
                    String msg = String.format(mContext.getString(
                            R.string.wifi_wps_connected), wifiInfo.getSSID());
                    updateDialog(DialogState.CONNECTED, msg);
                }
            }
        }
    }

最关键的还是wps流程了,这边onCreate方法里直接就是开始startWps流程了。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        mView = getLayoutInflater().inflate(R.layout.wifi_wps_dialog, null);

        mTextView = (TextView) mView.findViewById(R.id.wps_dialog_txt);
        mTextView.setText(R.string.wifi_wps_setup_msg);

        mTimeoutBar = ((ProgressBar) mView.findViewById(R.id.wps_timeout_bar));
        mTimeoutBar.setMax(WPS_TIMEOUT_S);
        mTimeoutBar.setProgress(0);

        mProgressBar = ((ProgressBar) mView.findViewById(R.id.wps_progress_bar));
        mProgressBar.setVisibility(View.GONE);

        mButton = ((Button) mView.findViewById(R.id.wps_dialog_btn));
        mButton.setText(R.string.wifi_cancel);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dismiss();
            }
        });

        mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);

        setView(mView);
        if (savedInstanceState == null) {
            startWps();
        }
        super.onCreate(savedInstanceState);
    }

    private void startWps() {
        WpsInfo wpsConfig = new WpsInfo();
        wpsConfig.setup = mWpsSetup;
        mWifiManager.startWps(wpsConfig, mWpsListener);
    }


调用到WifiManager.startWps

PS:

1)

/**
 * A class representing Wi-Fi Protected Setup
 *
 * {@see WifiP2pConfig}
 */
public class WpsInfo implements Parcelable {

    /** Push button configuration */
    public static final int PBC     = 0;
    /** Display pin method configuration - pin is generated and displayed on device */
    public static final int DISPLAY = 1;
    /** Keypad pin method configuration - pin is entered on device */
    public static final int KEYPAD  = 2;
    /** Label pin method configuration - pin is labelled on device */
    public static final int LABEL   = 3;
    /** Invalid configuration */
    public static final int INVALID = 4;

2)设置中的wps连接

wps按钮对应于WpsInfo.PBC,wps pin对应于WpsInfo.DISPLAY

WpsPreferenceController.java

    @Override
    public void displayPreference(PreferenceScreen screen) {
        super.displayPreference(screen);
        mWpsPushPref = screen.findPreference(KEY_WPS_PUSH);
        mWpsPinPref = screen.findPreference(KEY_WPS_PIN);
        if (mWpsPushPref == null || mWpsPinPref == null) {
            return;
        }
        // WpsDialog: Create the dialog like WifiSettings does.
        mWpsPushPref.setOnPreferenceClickListener((arg) -> {
                    WpsFragment wpsFragment = new WpsFragment(WpsInfo.PBC);
                    wpsFragment.show(mFragmentManager, KEY_WPS_PUSH);
                    return true;
                }
        );

        // WpsDialog: Create the dialog like WifiSettings does.
        mWpsPinPref.setOnPreferenceClickListener((arg) -> {
            WpsFragment wpsFragment = new WpsFragment(WpsInfo.DISPLAY);
            wpsFragment.show(mFragmentManager, KEY_WPS_PIN);
            return true;
        });
        togglePreferences();
    }

2.2 WifiManager

    /**
     * Start Wi-fi Protected Setup
     *
     * @param config WPS configuration (does not support {@link WpsInfo#LABEL})
     * @param listener for callbacks on success or failure. Can be null.
     * @throws IllegalStateException if the WifiManager instance needs to be
     * initialized again
     */
    public void startWps(WpsInfo config, WpsCallback listener) {
        if (config == null) throw new IllegalArgumentException("config cannot be null");
        getChannel().sendMessage(START_WPS, 0, putListener(listener), config);
    }

发送消息给服务端,也就是WifiServiceImpl

2.3 WifiServiceImpl

                case WifiManager.START_WPS:
                    if (checkChangePermissionAndReplyIfNotAuthorized(msg, WifiManager.WPS_FAILED)) {
                        mWifiStateMachine.sendMessage(Message.obtain(msg));
                    }
                    break;

发送给WifiStateMachine继续处理

2.4 WifiStateMachine

    class ConnectModeState extends State {
...
                case WifiManager.START_WPS:
                    WpsInfo wpsInfo = (WpsInfo) message.obj;
                    if (wpsInfo == null) {
                        loge("Cannot start WPS with null WpsInfo object");
                        replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.ERROR);
                        break;
                    }
                    WpsResult wpsResult = new WpsResult();
                    // TODO(b/32898136): Not needed when we start deleting networks from supplicant
                    // on disconnect.
                    if (!mWifiNative.removeAllNetworks()) {
                        loge("Failed to remove networks before WPS");
                    }
                    switch (wpsInfo.setup) {
                        case WpsInfo.PBC:
                            if (mWifiNative.startWpsPbc(wpsInfo.BSSID)) {
                                wpsResult.status = WpsResult.Status.SUCCESS;
                            } else {
                                Log.e(TAG, "Failed to start WPS push button configuration");
                                wpsResult.status = WpsResult.Status.FAILURE;
                            }
                            break;
                        case WpsInfo.KEYPAD:
                            if (mWifiNative.startWpsRegistrar(wpsInfo.BSSID, wpsInfo.pin)) {
                                wpsResult.status = WpsResult.Status.SUCCESS;
                            } else {
                                Log.e(TAG, "Failed to start WPS push button configuration");
                                wpsResult.status = WpsResult.Status.FAILURE;
                            }
                            break;
                        case WpsInfo.DISPLAY:
                            wpsResult.pin = mWifiNative.startWpsPinDisplay(wpsInfo.BSSID);
                            if (!TextUtils.isEmpty(wpsResult.pin)) {
                                wpsResult.status = WpsResult.Status.SUCCESS;
                            } else {
                                Log.e(TAG, "Failed to start WPS pin method configuration");
                                wpsResult.status = WpsResult.Status.FAILURE;
                            }
                            break;
                        default:
                            wpsResult = new WpsResult(Status.FAILURE);
                            loge("Invalid setup for WPS");
                            break;
                    }
                    if (wpsResult.status == Status.SUCCESS) {
                        replyToMessage(message, WifiManager.START_WPS_SUCCEEDED, wpsResult);
                        transitionTo(mWpsRunningState);
                    } else {
                        loge("Failed to start WPS with config " + wpsInfo.toString());
                        replyToMessage(message, WifiManager.WPS_FAILED, WifiManager.ERROR);
                    }
                    break;

这边启动成功就会发送一个WifiManager.START_WPS_SUCCEEDED给WifiManager,WifiManager继而会回调设置传来的listener的onStarted方法。

                case WifiManager.START_WPS_SUCCEEDED:
                    if (listener != null) {
                        WpsResult result = (WpsResult) message.obj;
                        ((WpsCallback) listener).onStarted(result.pin);
                        //Listener needs to stay until completion or failure
                        synchronized (mListenerMapLock) {
                            mListenerMap.put(message.arg2, listener);
                        }
                    }
                    break;

start完了后切换到WpsRunningState

    /**
     * WPS connection flow:
     * 1. WifiStateMachine receive WPS_START message from WifiManager API.
     * 2. WifiStateMachine initiates the appropriate WPS operation using WifiNative methods:
     * {@link WifiNative#startWpsPbc(String)}, {@link WifiNative#startWpsPinDisplay(String)}, etc.
     * 3. WifiStateMachine then transitions to this WpsRunningState.
     * 4a. Once WifiStateMachine receive the connected event:
     * {@link WifiMonitor#NETWORK_CONNECTION_EVENT},
     * 4a.1 Load the network params out of wpa_supplicant.
     * 4a.2 Add the network with params to WifiConfigManager.
     * 4a.3 Enable the network with |disableOthers| set to true.
     * 4a.4 Send a response to the original source of WifiManager API using {@link #mSourceMessage}.
     * 4b. Any failures are notified to the original source of WifiManager API
     * using {@link #mSourceMessage}.
     * 5. We then transition to disconnected state and let network selection reconnect to the newly
     * added network.
     */
    class WpsRunningState extends State {
        // Tracks the source to provide a reply
        private Message mSourceMessage;
        @Override
        public void enter() {
            mSourceMessage = Message.obtain(getCurrentMessage());
        }
        @Override
        public boolean processMessage(Message message) {
            logStateAndMessage(message, this);

            switch (message.what) {
                case WifiMonitor.WPS_SUCCESS_EVENT:
                    // Ignore intermediate success, wait for full connection
                    break;
                case WifiMonitor.NETWORK_CONNECTION_EVENT:
                    Pair<Boolean, Integer> loadResult = loadNetworksFromSupplicantAfterWps();
                    boolean success = loadResult.first;
                    int netId = loadResult.second;
                    if (success) {
                        message.arg1 = netId;
                        replyToMessage(mSourceMessage, WifiManager.WPS_COMPLETED);
                    } else {
                        replyToMessage(mSourceMessage, WifiManager.WPS_FAILED,
                                WifiManager.ERROR);
                    }
                    mSourceMessage.recycle();
                    mSourceMessage = null;
                    deferMessage(message);
                    transitionTo(mDisconnectedState);
                    break;

如注释所述,接收到NETWORK_CONNECTION_EVENT消息后表示wps已完成,后续就开始和一般WiFi连接一样的流程,加载网络配置,启用连接的网络并禁用其他的,发送WifiManager.WPS_COMPLETED消息告诉WifiManager,WifiManager相应地回调设置传来的listener的onSucceeded()方法。

                case WifiManager.WPS_COMPLETED:
                    if (listener != null) {
                        ((WpsCallback) listener).onSucceeded();
                    }
                    break;

dhcp走完了后网络状态会设为connected,这时设置接收到广播消息,将wps流程结束

        } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
            NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(
                    WifiManager.EXTRA_NETWORK_INFO);
            final NetworkInfo.DetailedState state = info.getDetailedState();
            if (state == DetailedState.CONNECTED &&
                    mDialogState == DialogState.WPS_COMPLETE) {
                WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
                if (wifiInfo != null) {
                    String msg = String.format(mContext.getString(
                            R.string.wifi_wps_connected), wifiInfo.getSSID());
                    updateDialog(DialogState.CONNECTED, msg);
                }
            }
        }

3.总结

对比wps连接和普通输密码流程,其实流程不同在校验上,校验完成后回到WifiStateMachine还是dhcp一套后就连接上了,话说连接流程到supplicant还是看的很痛苦,待续。。。

猜你喜欢

转载自blog.csdn.net/sinat_20059415/article/details/83036128