WifiSettings.java revision 22d441acdba0fb7b99f5958ddd0198f4c371b2a7
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.settings.wifi;
18
19import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
20
21import android.app.ActionBar;
22import android.app.Activity;
23import android.app.AlertDialog;
24import android.app.Dialog;
25import android.content.BroadcastReceiver;
26import android.content.Context;
27import android.content.DialogInterface;
28import android.content.Intent;
29import android.content.IntentFilter;
30import android.net.ConnectivityManager;
31import android.net.NetworkInfo;
32import android.net.NetworkInfo.DetailedState;
33import android.net.wifi.ScanResult;
34import android.net.wifi.SupplicantState;
35import android.net.wifi.WifiConfiguration;
36import android.net.wifi.WifiConfiguration.KeyMgmt;
37import android.net.wifi.WifiInfo;
38import android.net.wifi.WifiManager;
39import android.net.wifi.WpsResult;
40import android.os.Bundle;
41import android.os.Handler;
42import android.os.Message;
43import android.preference.Preference;
44import android.preference.PreferenceActivity;
45import android.preference.PreferenceScreen;
46import android.security.Credentials;
47import android.security.KeyStore;
48import android.util.Log;
49import android.view.ContextMenu;
50import android.view.ContextMenu.ContextMenuInfo;
51import android.view.Gravity;
52import android.view.LayoutInflater;
53import android.view.Menu;
54import android.view.MenuInflater;
55import android.view.MenuItem;
56import android.view.View;
57import android.view.ViewGroup;
58import android.widget.AdapterView.AdapterContextMenuInfo;
59import android.widget.Switch;
60import android.widget.TextView;
61import android.widget.Toast;
62
63import com.android.internal.util.AsyncChannel;
64import com.android.settings.R;
65import com.android.settings.SettingsPreferenceFragment;
66
67import java.util.ArrayList;
68import java.util.Collection;
69import java.util.List;
70import java.util.concurrent.atomic.AtomicBoolean;
71
72/**
73 * This currently provides three types of UI.
74 *
75 * Two are for phones with relatively small screens: "for SetupWizard" and "for usual Settings".
76 * Users just need to launch WifiSettings Activity as usual. The request will be appropriately
77 * handled by ActivityManager, and they will have appropriate look-and-feel with this fragment.
78 *
79 * Third type is for Setup Wizard with X-Large, landscape UI. Users need to launch
80 * {@link WifiSettingsForSetupWizardXL} Activity, which contains this fragment but also has
81 * other decorations specific to that screen.
82 */
83public class WifiSettings extends SettingsPreferenceFragment
84        implements DialogInterface.OnClickListener  {
85    private static final String TAG = "WifiSettings";
86    private static final int MENU_ID_SCAN = Menu.FIRST;
87    private static final int MENU_ID_ADD_NETWORK = Menu.FIRST + 1;
88    private static final int MENU_ID_ADVANCED = Menu.FIRST + 2;
89    private static final int MENU_ID_CONNECT = Menu.FIRST + 3;
90    private static final int MENU_ID_FORGET = Menu.FIRST + 4;
91    private static final int MENU_ID_MODIFY = Menu.FIRST + 5;
92
93    private static final int WIFI_DIALOG_ID = 1;
94
95    // Instance state keys
96    private static final String SAVE_DIALOG_EDIT_MODE = "edit_mode";
97    private static final String SAVE_DIALOG_ACCESS_POINT_STATE = "wifi_ap_state";
98
99    private final IntentFilter mFilter;
100    private final BroadcastReceiver mReceiver;
101    private final Scanner mScanner;
102
103    private WifiManager mWifiManager;
104    private WifiEnabler mWifiEnabler;
105    // An access point being editted is stored here.
106    private AccessPoint mSelectedAccessPoint;
107
108    private DetailedState mLastState;
109    private WifiInfo mLastInfo;
110
111    private AtomicBoolean mConnected = new AtomicBoolean(false);
112
113    private int mKeyStoreNetworkId = INVALID_NETWORK_ID;
114
115    private WifiDialog mDialog;
116
117    private TextView mEmptyView;
118
119    /* Used in Wifi Setup context */
120
121    // this boolean extra specifies whether to disable the Next button when not connected
122    private static final String EXTRA_ENABLE_NEXT_ON_CONNECT = "wifi_enable_next_on_connect";
123
124    // should Next button only be enabled when we have a connection?
125    private boolean mEnableNextOnConnection;
126    private boolean mInXlSetupWizard;
127
128    // Save the dialog details
129    private boolean mDlgEdit;
130    private AccessPoint mDlgAccessPoint;
131    private Bundle mAccessPointSavedState;
132
133    /* End of "used in Wifi Setup context" */
134
135    public WifiSettings() {
136        mFilter = new IntentFilter();
137        mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
138        mFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
139        mFilter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION);
140        mFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
141        mFilter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
142        mFilter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
143        mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
144        mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
145        mFilter.addAction(WifiManager.ERROR_ACTION);
146
147        mReceiver = new BroadcastReceiver() {
148            @Override
149            public void onReceive(Context context, Intent intent) {
150                handleEvent(context, intent);
151            }
152        };
153
154        mScanner = new Scanner();
155    }
156
157    @Override
158    public void onAttach(Activity activity) {
159        super.onAttach(activity);
160
161        mInXlSetupWizard = (activity instanceof WifiSettingsForSetupWizardXL);
162    }
163
164    @Override
165    public void onActivityCreated(Bundle savedInstanceState) {
166        // We don't call super.onActivityCreated() here, since it assumes we already set up
167        // Preference (probably in onCreate()), while WifiSettings exceptionally set it up in
168        // this method.
169
170        mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
171        mWifiManager.asyncConnect(getActivity(), new WifiServiceHandler());
172        if (savedInstanceState != null
173                && savedInstanceState.containsKey(SAVE_DIALOG_ACCESS_POINT_STATE)) {
174            mDlgEdit = savedInstanceState.getBoolean(SAVE_DIALOG_EDIT_MODE);
175            mAccessPointSavedState = savedInstanceState.getBundle(SAVE_DIALOG_ACCESS_POINT_STATE);
176        }
177
178        final Activity activity = getActivity();
179        final Intent intent = activity.getIntent();
180
181        // if we're supposed to enable/disable the Next button based on our current connection
182        // state, start it off in the right state
183        mEnableNextOnConnection = intent.getBooleanExtra(EXTRA_ENABLE_NEXT_ON_CONNECT, false);
184
185        if (mEnableNextOnConnection) {
186            if (hasNextButton()) {
187                final ConnectivityManager connectivity = (ConnectivityManager)
188                        getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
189                if (connectivity != null) {
190                    NetworkInfo info = connectivity.getNetworkInfo(
191                            ConnectivityManager.TYPE_WIFI);
192                    changeNextButtonState(info.isConnected());
193                }
194            }
195        }
196
197        if (mInXlSetupWizard) {
198            addPreferencesFromResource(R.xml.wifi_access_points_for_wifi_setup_xl);
199        } else {
200            addPreferencesFromResource(R.xml.wifi_settings);
201
202            Switch actionBarSwitch = new Switch(activity);
203
204            if (activity instanceof PreferenceActivity) {
205                PreferenceActivity preferenceActivity = (PreferenceActivity) activity;
206                if (preferenceActivity.onIsHidingHeaders() || !preferenceActivity.onIsMultiPane()) {
207                    final int padding = activity.getResources().getDimensionPixelSize(
208                            R.dimen.action_bar_switch_padding);
209                    actionBarSwitch.setPadding(0, 0, padding, 0);
210                    activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
211                            ActionBar.DISPLAY_SHOW_CUSTOM);
212                    activity.getActionBar().setCustomView(actionBarSwitch, new ActionBar.LayoutParams(
213                            ActionBar.LayoutParams.WRAP_CONTENT,
214                            ActionBar.LayoutParams.WRAP_CONTENT,
215                            Gravity.CENTER_VERTICAL | Gravity.RIGHT));
216                }
217            }
218
219            mWifiEnabler = new WifiEnabler(activity, actionBarSwitch);
220        }
221
222        mEmptyView = (TextView) getView().findViewById(android.R.id.empty);
223        getListView().setEmptyView(mEmptyView);
224
225        registerForContextMenu(getListView());
226        setHasOptionsMenu(true);
227
228        // After confirming PreferenceScreen is available, we call super.
229        super.onActivityCreated(savedInstanceState);
230    }
231
232    @Override
233    public void onResume() {
234        super.onResume();
235        if (mWifiEnabler != null) {
236            mWifiEnabler.resume();
237        }
238
239        getActivity().registerReceiver(mReceiver, mFilter);
240        if (mKeyStoreNetworkId != INVALID_NETWORK_ID &&
241                KeyStore.getInstance().state() == KeyStore.State.UNLOCKED) {
242            mWifiManager.connectNetwork(mKeyStoreNetworkId);
243        }
244        mKeyStoreNetworkId = INVALID_NETWORK_ID;
245
246        updateAccessPoints();
247    }
248
249    @Override
250    public void onPause() {
251        super.onPause();
252        if (mWifiEnabler != null) {
253            mWifiEnabler.pause();
254        }
255        getActivity().unregisterReceiver(mReceiver);
256        mScanner.pause();
257    }
258
259    @Override
260    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
261        // We don't want menus in Setup Wizard XL.
262        if (!mInXlSetupWizard) {
263            final boolean wifiIsEnabled = mWifiManager.isWifiEnabled();
264            menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.wifi_menu_scan)
265                    //.setIcon(R.drawable.ic_menu_scan_network)
266                    .setEnabled(wifiIsEnabled)
267                    .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
268            menu.add(Menu.NONE, MENU_ID_ADD_NETWORK, 0, R.string.wifi_add_network)
269                    .setEnabled(wifiIsEnabled)
270                    .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
271            menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.wifi_menu_advanced)
272                    //.setIcon(android.R.drawable.ic_menu_manage)
273                    .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
274        }
275        super.onCreateOptionsMenu(menu, inflater);
276    }
277
278    @Override
279    public void onSaveInstanceState(Bundle outState) {
280        super.onSaveInstanceState(outState);
281
282        // If the dialog is showing, save its state.
283        if (mDialog != null && mDialog.isShowing()) {
284            outState.putBoolean(SAVE_DIALOG_EDIT_MODE, mDlgEdit);
285            if (mDlgAccessPoint != null) {
286                mAccessPointSavedState = new Bundle();
287                mDlgAccessPoint.saveWifiState(mAccessPointSavedState);
288                outState.putBundle(SAVE_DIALOG_ACCESS_POINT_STATE, mAccessPointSavedState);
289            }
290        }
291    }
292
293    @Override
294    public boolean onOptionsItemSelected(MenuItem item) {
295        switch (item.getItemId()) {
296            case MENU_ID_SCAN:
297                if (mWifiManager.isWifiEnabled()) {
298                    mScanner.forceScan();
299                }
300                return true;
301            case MENU_ID_ADD_NETWORK:
302                if (mWifiManager.isWifiEnabled()) {
303                    onAddNetworkPressed();
304                }
305                return true;
306            case MENU_ID_ADVANCED:
307                if (getActivity() instanceof PreferenceActivity) {
308                    ((PreferenceActivity) getActivity()).startPreferencePanel(
309                            AdvancedWifiSettings.class.getCanonicalName(),
310                            null,
311                            R.string.wifi_advanced_titlebar, null,
312                            this, 0);
313                } else {
314                    startFragment(this, AdvancedWifiSettings.class.getCanonicalName(), -1, null);
315                }
316                return true;
317        }
318        return super.onOptionsItemSelected(item);
319    }
320
321    @Override
322    public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) {
323        if (mInXlSetupWizard) {
324            ((WifiSettingsForSetupWizardXL)getActivity()).onCreateContextMenu(menu, view, info);
325        } else if (info instanceof AdapterContextMenuInfo) {
326            Preference preference = (Preference) getListView().getItemAtPosition(
327                    ((AdapterContextMenuInfo) info).position);
328
329            if (preference instanceof AccessPoint) {
330                mSelectedAccessPoint = (AccessPoint) preference;
331                menu.setHeaderTitle(mSelectedAccessPoint.ssid);
332                if (mSelectedAccessPoint.getLevel() != -1
333                        && mSelectedAccessPoint.getState() == null) {
334                    menu.add(Menu.NONE, MENU_ID_CONNECT, 0, R.string.wifi_menu_connect);
335                }
336                if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
337                    menu.add(Menu.NONE, MENU_ID_FORGET, 0, R.string.wifi_menu_forget);
338                    menu.add(Menu.NONE, MENU_ID_MODIFY, 0, R.string.wifi_menu_modify);
339                }
340            }
341        }
342    }
343
344    @Override
345    public boolean onContextItemSelected(MenuItem item) {
346        if (mSelectedAccessPoint == null) {
347            return super.onContextItemSelected(item);
348        }
349        switch (item.getItemId()) {
350            case MENU_ID_CONNECT: {
351                if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
352                    if (!requireKeyStore(mSelectedAccessPoint.getConfig())) {
353                        mWifiManager.connectNetwork(mSelectedAccessPoint.networkId);
354                    }
355                } else if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE) {
356                    /** Bypass dialog for unsecured networks */
357                    mSelectedAccessPoint.generateOpenNetworkConfig();
358                    mWifiManager.connectNetwork(mSelectedAccessPoint.getConfig());
359                } else {
360                    showConfigUi(mSelectedAccessPoint, true);
361                }
362                return true;
363            }
364            case MENU_ID_FORGET: {
365                mWifiManager.forgetNetwork(mSelectedAccessPoint.networkId);
366                return true;
367            }
368            case MENU_ID_MODIFY: {
369                showConfigUi(mSelectedAccessPoint, true);
370                return true;
371            }
372        }
373        return super.onContextItemSelected(item);
374    }
375
376    @Override
377    public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {
378        if (preference instanceof AccessPoint) {
379            mSelectedAccessPoint = (AccessPoint) preference;
380            /** Bypass dialog for unsecured, unsaved networks */
381            if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE &&
382                    mSelectedAccessPoint.networkId == INVALID_NETWORK_ID) {
383                mSelectedAccessPoint.generateOpenNetworkConfig();
384                mWifiManager.connectNetwork(mSelectedAccessPoint.getConfig());
385            } else {
386                showConfigUi(mSelectedAccessPoint, false);
387            }
388        } else {
389            return super.onPreferenceTreeClick(screen, preference);
390        }
391        return true;
392    }
393
394    /**
395     * Shows an appropriate Wifi configuration component.
396     * Called when a user clicks "Add network" preference or one of available networks is selected.
397     */
398    private void showConfigUi(AccessPoint accessPoint, boolean edit) {
399        if (mInXlSetupWizard) {
400            ((WifiSettingsForSetupWizardXL)getActivity()).showConfigUi(accessPoint, edit);
401        } else {
402            showDialog(accessPoint, edit);
403        }
404    }
405
406    private void showDialog(AccessPoint accessPoint, boolean edit) {
407        if (mDialog != null) {
408            removeDialog(WIFI_DIALOG_ID);
409            mDialog = null;
410        }
411
412        // Save the access point and edit mode
413        mDlgAccessPoint = accessPoint;
414        mDlgEdit = edit;
415
416        showDialog(WIFI_DIALOG_ID);
417    }
418
419    @Override
420    public Dialog onCreateDialog(int dialogId) {
421        AccessPoint ap = mDlgAccessPoint; // For manual launch
422        if (ap == null) { // For re-launch from saved state
423            if (mAccessPointSavedState != null) {
424                ap = new AccessPoint(getActivity(), mAccessPointSavedState);
425                // For repeated orientation changes
426                mDlgAccessPoint = ap;
427            }
428        }
429        // If it's still null, fine, it's for Add Network
430        mSelectedAccessPoint = ap;
431        mDialog = new WifiDialog(getActivity(), this, ap, mDlgEdit);
432        return mDialog;
433    }
434
435    private boolean requireKeyStore(WifiConfiguration config) {
436        if (WifiConfigController.requireKeyStore(config) &&
437                KeyStore.getInstance().state() != KeyStore.State.UNLOCKED) {
438            mKeyStoreNetworkId = config.networkId;
439            Credentials.getInstance().unlock(getActivity());
440            return true;
441        }
442        return false;
443    }
444
445    /**
446     * Shows the latest access points available with supplimental information like
447     * the strength of network and the security for it.
448     */
449    private void updateAccessPoints() {
450        final int wifiState = mWifiManager.getWifiState();
451
452        switch (wifiState) {
453            case WifiManager.WIFI_STATE_ENABLED:
454                getPreferenceScreen().removeAll();
455                // AccessPoints are automatically sorted with TreeSet.
456                final Collection<AccessPoint> accessPoints = constructAccessPoints();
457                if (mInXlSetupWizard) {
458                    ((WifiSettingsForSetupWizardXL)getActivity()).onAccessPointsUpdated(
459                            getPreferenceScreen(), accessPoints);
460                } else {
461                    for (AccessPoint accessPoint : accessPoints) {
462                        getPreferenceScreen().addPreference(accessPoint);
463                    }
464                }
465                break;
466
467            case WifiManager.WIFI_STATE_ENABLING:
468                getPreferenceScreen().removeAll();
469                break;
470
471            case WifiManager.WIFI_STATE_DISABLING:
472                addMessagePreference(R.string.wifi_stopping);
473                break;
474
475            case WifiManager.WIFI_STATE_DISABLED:
476                addMessagePreference(R.string.wifi_empty_list_wifi_off);
477                break;
478        }
479    }
480
481    private void addMessagePreference(int messageId) {
482        if (mEmptyView != null) mEmptyView.setText(messageId);
483        getPreferenceScreen().removeAll();
484    }
485
486    private Collection<AccessPoint> constructAccessPoints() {
487        Collection<AccessPoint> accessPoints = new ArrayList<AccessPoint>();
488
489        final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
490        if (configs != null) {
491            for (WifiConfiguration config : configs) {
492                AccessPoint accessPoint = new AccessPoint(getActivity(), config);
493                accessPoint.update(mLastInfo, mLastState);
494                accessPoints.add(accessPoint);
495            }
496        }
497
498        final List<ScanResult> results = mWifiManager.getScanResults();
499        if (results != null) {
500            for (ScanResult result : results) {
501                // Ignore hidden and ad-hoc networks.
502                if (result.SSID == null || result.SSID.length() == 0 ||
503                        result.capabilities.contains("[IBSS]")) {
504                    continue;
505                }
506
507                boolean found = false;
508                for (AccessPoint accessPoint : accessPoints) {
509                    if (accessPoint.update(result)) {
510                        found = true;
511                        break;
512                    }
513                }
514                if (!found) {
515                    accessPoints.add(new AccessPoint(getActivity(), result));
516                }
517            }
518        }
519
520        return accessPoints;
521    }
522
523    private void handleEvent(Context context, Intent intent) {
524        String action = intent.getAction();
525        if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
526            updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
527                    WifiManager.WIFI_STATE_UNKNOWN));
528        } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action) ||
529                WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action) ||
530                WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) {
531                updateAccessPoints();
532        } else if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)) {
533            //Ignore supplicant state changes when network is connected
534            //TODO: we should deprecate SUPPLICANT_STATE_CHANGED_ACTION and
535            //introduce a broadcast that combines the supplicant and network
536            //network state change events so the apps dont have to worry about
537            //ignoring supplicant state change when network is connected
538            //to get more fine grained information.
539            if (!mConnected.get()) {
540                updateConnectionState(WifiInfo.getDetailedStateOf((SupplicantState)
541                        intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE)));
542            }
543
544            if (mInXlSetupWizard) {
545                ((WifiSettingsForSetupWizardXL)getActivity()).onSupplicantStateChanged(intent);
546            }
547        } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
548            NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(
549                    WifiManager.EXTRA_NETWORK_INFO);
550            mConnected.set(info.isConnected());
551            changeNextButtonState(info.isConnected());
552            updateAccessPoints();
553            updateConnectionState(info.getDetailedState());
554        } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {
555            updateConnectionState(null);
556        } else if (WifiManager.ERROR_ACTION.equals(action)) {
557            int errorCode = intent.getIntExtra(WifiManager.EXTRA_ERROR_CODE, 0);
558            switch (errorCode) {
559                case WifiManager.WPS_OVERLAP_ERROR:
560                    Toast.makeText(context, R.string.wifi_wps_overlap_error,
561                            Toast.LENGTH_SHORT).show();
562                    break;
563            }
564        }
565    }
566
567    private void updateConnectionState(DetailedState state) {
568        /* sticky broadcasts can call this when wifi is disabled */
569        if (!mWifiManager.isWifiEnabled()) {
570            mScanner.pause();
571            return;
572        }
573
574        if (state == DetailedState.OBTAINING_IPADDR) {
575            mScanner.pause();
576        } else {
577            mScanner.resume();
578        }
579
580        mLastInfo = mWifiManager.getConnectionInfo();
581        if (state != null) {
582            mLastState = state;
583        }
584
585        for (int i = getPreferenceScreen().getPreferenceCount() - 1; i >= 0; --i) {
586            // Maybe there's a WifiConfigPreference
587            Preference preference = getPreferenceScreen().getPreference(i);
588            if (preference instanceof AccessPoint) {
589                final AccessPoint accessPoint = (AccessPoint) preference;
590                accessPoint.update(mLastInfo, mLastState);
591            }
592        }
593
594        if (mInXlSetupWizard) {
595            ((WifiSettingsForSetupWizardXL)getActivity()).updateConnectionState(mLastState);
596        }
597    }
598
599    private void updateWifiState(int state) {
600        getActivity().invalidateOptionsMenu();
601
602        switch (state) {
603            case WifiManager.WIFI_STATE_ENABLED:
604                mScanner.resume();
605                return; // not break, to avoid the call to pause() below
606
607            case WifiManager.WIFI_STATE_ENABLING:
608                addMessagePreference(R.string.wifi_starting);
609                break;
610
611            case WifiManager.WIFI_STATE_DISABLED:
612                addMessagePreference(R.string.wifi_empty_list_wifi_off);
613                break;
614        }
615
616        mLastInfo = null;
617        mLastState = null;
618        mScanner.pause();
619    }
620
621    private class Scanner extends Handler {
622        private int mRetry = 0;
623
624        void resume() {
625            if (!hasMessages(0)) {
626                sendEmptyMessage(0);
627            }
628        }
629
630        void forceScan() {
631            removeMessages(0);
632            sendEmptyMessage(0);
633        }
634
635        void pause() {
636            mRetry = 0;
637            removeMessages(0);
638        }
639
640        @Override
641        public void handleMessage(Message message) {
642            if (mWifiManager.startScanActive()) {
643                mRetry = 0;
644            } else if (++mRetry >= 3) {
645                mRetry = 0;
646                Toast.makeText(getActivity(), R.string.wifi_fail_to_scan,
647                        Toast.LENGTH_LONG).show();
648                return;
649            }
650            // Combo scans can take 5-6s to complete. Increase interval to 10s.
651            sendEmptyMessageDelayed(0, 10000);
652        }
653    }
654
655    private class WifiServiceHandler extends Handler {
656
657        @Override
658        public void handleMessage(Message msg) {
659            switch (msg.what) {
660                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
661                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
662                        //AsyncChannel in msg.obj
663                    } else {
664                        //AsyncChannel set up failure, ignore
665                        Log.e(TAG, "Failed to establish AsyncChannel connection");
666                    }
667                    break;
668                case WifiManager.CMD_WPS_COMPLETED:
669                    WpsResult result = (WpsResult) msg.obj;
670                    if (result == null) break;
671                    AlertDialog.Builder dialog = new AlertDialog.Builder(getActivity())
672                        .setTitle(R.string.wifi_wps_setup_title)
673                        .setPositiveButton(android.R.string.ok, null);
674                    switch (result.status) {
675                        case FAILURE:
676                            dialog.setMessage(R.string.wifi_wps_failed);
677                            dialog.show();
678                            break;
679                        case IN_PROGRESS:
680                            dialog.setMessage(R.string.wifi_wps_in_progress);
681                            dialog.show();
682                            break;
683                        default:
684                            if (result.pin != null) {
685                                dialog.setMessage(getResources().getString(
686                                        R.string.wifi_wps_pin_output, result.pin));
687                                dialog.show();
688                            }
689                            break;
690                    }
691                    break;
692                //TODO: more connectivity feedback
693                default:
694                    //Ignore
695                    break;
696            }
697        }
698    }
699
700    /**
701     * Renames/replaces "Next" button when appropriate. "Next" button usually exists in
702     * Wifi setup screens, not in usual wifi settings screen.
703     *
704     * @param connected true when the device is connected to a wifi network.
705     */
706    private void changeNextButtonState(boolean connected) {
707        if (mInXlSetupWizard) {
708            ((WifiSettingsForSetupWizardXL)getActivity()).changeNextButtonState(connected);
709        } else if (mEnableNextOnConnection && hasNextButton()) {
710            getNextButton().setEnabled(connected);
711        }
712    }
713
714    public void onClick(DialogInterface dialogInterface, int button) {
715        if (mInXlSetupWizard) {
716            if (button == WifiDialog.BUTTON_FORGET && mSelectedAccessPoint != null) {
717                forget();
718            } else if (button == WifiDialog.BUTTON_SUBMIT) {
719                ((WifiSettingsForSetupWizardXL)getActivity()).onConnectButtonPressed();
720            }
721        } else {
722            if (button == WifiDialog.BUTTON_FORGET && mSelectedAccessPoint != null) {
723                forget();
724            } else if (button == WifiDialog.BUTTON_SUBMIT) {
725                submit(mDialog.getController());
726            }
727        }
728
729    }
730
731    /* package */ void submit(WifiConfigController configController) {
732        int networkSetup = configController.chosenNetworkSetupMethod();
733        switch(networkSetup) {
734            case WifiConfigController.WPS_PBC:
735            case WifiConfigController.WPS_DISPLAY:
736            case WifiConfigController.WPS_KEYPAD:
737                mWifiManager.startWps(configController.getWpsConfig());
738                break;
739            case WifiConfigController.MANUAL:
740                final WifiConfiguration config = configController.getConfig();
741
742                if (config == null) {
743                    if (mSelectedAccessPoint != null
744                            && !requireKeyStore(mSelectedAccessPoint.getConfig())
745                            && mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
746                        mWifiManager.connectNetwork(mSelectedAccessPoint.networkId);
747                    }
748                } else if (config.networkId != INVALID_NETWORK_ID) {
749                    if (mSelectedAccessPoint != null) {
750                        saveNetwork(config);
751                    }
752                } else {
753                    if (configController.isEdit() || requireKeyStore(config)) {
754                        saveNetwork(config);
755                    } else {
756                        mWifiManager.connectNetwork(config);
757                    }
758                }
759                break;
760        }
761
762        if (mWifiManager.isWifiEnabled()) {
763            mScanner.resume();
764        }
765        updateAccessPoints();
766    }
767
768    private void saveNetwork(WifiConfiguration config) {
769        if (mInXlSetupWizard) {
770            ((WifiSettingsForSetupWizardXL)getActivity()).onSaveNetwork(config);
771        } else {
772            mWifiManager.saveNetwork(config);
773        }
774    }
775
776    /* package */ void forget() {
777        mWifiManager.forgetNetwork(mSelectedAccessPoint.networkId);
778
779        if (mWifiManager.isWifiEnabled()) {
780            mScanner.resume();
781        }
782        updateAccessPoints();
783
784        // We need to rename/replace "Next" button in wifi setup context.
785        changeNextButtonState(false);
786    }
787
788    /**
789     * Refreshes acccess points and ask Wifi module to scan networks again.
790     */
791    /* package */ void refreshAccessPoints() {
792        if (mWifiManager.isWifiEnabled()) {
793            mScanner.resume();
794        }
795
796        getPreferenceScreen().removeAll();
797    }
798
799    /**
800     * Called when "add network" button is pressed.
801     */
802    /* package */ void onAddNetworkPressed() {
803        // No exact access point is selected.
804        mSelectedAccessPoint = null;
805        showConfigUi(null, true);
806    }
807
808    /* package */ int getAccessPointsCount() {
809        final boolean wifiIsEnabled = mWifiManager.isWifiEnabled();
810        if (wifiIsEnabled) {
811            return getPreferenceScreen().getPreferenceCount();
812        } else {
813            return 0;
814        }
815    }
816
817    /**
818     * Requests wifi module to pause wifi scan. May be ignored when the module is disabled.
819     */
820    /* package */ void pauseWifiScan() {
821        if (mWifiManager.isWifiEnabled()) {
822            mScanner.pause();
823        }
824    }
825
826    /**
827     * Requests wifi module to resume wifi scan. May be ignored when the module is disabled.
828     */
829    /* package */ void resumeWifiScan() {
830        if (mWifiManager.isWifiEnabled()) {
831            mScanner.resume();
832        }
833    }
834}
835