WifiSettings.java revision 348b62e6e9568166a17b1304296e0f08aee97cf5
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_NEVER);
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                    // Shortcut for open networks.
357                    WifiConfiguration config = new WifiConfiguration();
358                    config.SSID = AccessPoint.convertToQuotedString(mSelectedAccessPoint.ssid);
359                    config.allowedKeyManagement.set(KeyMgmt.NONE);
360                    mWifiManager.connectNetwork(config);
361                } else {
362                    showConfigUi(mSelectedAccessPoint, true);
363                }
364                return true;
365            }
366            case MENU_ID_FORGET: {
367                mWifiManager.forgetNetwork(mSelectedAccessPoint.networkId);
368                return true;
369            }
370            case MENU_ID_MODIFY: {
371                showConfigUi(mSelectedAccessPoint, true);
372                return true;
373            }
374        }
375        return super.onContextItemSelected(item);
376    }
377
378    @Override
379    public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {
380        if (preference instanceof AccessPoint) {
381            mSelectedAccessPoint = (AccessPoint) preference;
382            showConfigUi(mSelectedAccessPoint, false);
383        } else {
384            return super.onPreferenceTreeClick(screen, preference);
385        }
386        return true;
387    }
388
389    /**
390     * Shows an appropriate Wifi configuration component.
391     * Called when a user clicks "Add network" preference or one of available networks is selected.
392     */
393    private void showConfigUi(AccessPoint accessPoint, boolean edit) {
394        if (mInXlSetupWizard) {
395            ((WifiSettingsForSetupWizardXL)getActivity()).showConfigUi(accessPoint, edit);
396        } else {
397            showDialog(accessPoint, edit);
398        }
399    }
400
401    private void showDialog(AccessPoint accessPoint, boolean edit) {
402        if (mDialog != null) {
403            removeDialog(WIFI_DIALOG_ID);
404            mDialog = null;
405        }
406
407        // Save the access point and edit mode
408        mDlgAccessPoint = accessPoint;
409        mDlgEdit = edit;
410
411        showDialog(WIFI_DIALOG_ID);
412    }
413
414    @Override
415    public Dialog onCreateDialog(int dialogId) {
416        AccessPoint ap = mDlgAccessPoint; // For manual launch
417        if (ap == null) { // For re-launch from saved state
418            if (mAccessPointSavedState != null) {
419                ap = new AccessPoint(getActivity(), mAccessPointSavedState);
420                // For repeated orientation changes
421                mDlgAccessPoint = ap;
422            }
423        }
424        // If it's still null, fine, it's for Add Network
425        mSelectedAccessPoint = ap;
426        mDialog = new WifiDialog(getActivity(), this, ap, mDlgEdit);
427        return mDialog;
428    }
429
430    private boolean requireKeyStore(WifiConfiguration config) {
431        if (WifiConfigController.requireKeyStore(config) &&
432                KeyStore.getInstance().state() != KeyStore.State.UNLOCKED) {
433            mKeyStoreNetworkId = config.networkId;
434            Credentials.getInstance().unlock(getActivity());
435            return true;
436        }
437        return false;
438    }
439
440    /**
441     * Shows the latest access points available with supplimental information like
442     * the strength of network and the security for it.
443     */
444    private void updateAccessPoints() {
445        final int wifiState = mWifiManager.getWifiState();
446
447        switch (wifiState) {
448            case WifiManager.WIFI_STATE_ENABLED:
449                getPreferenceScreen().removeAll();
450                // AccessPoints are automatically sorted with TreeSet.
451                final Collection<AccessPoint> accessPoints = constructAccessPoints();
452                if (mInXlSetupWizard) {
453                    ((WifiSettingsForSetupWizardXL)getActivity()).onAccessPointsUpdated(
454                            getPreferenceScreen(), accessPoints);
455                } else {
456                    for (AccessPoint accessPoint : accessPoints) {
457                        getPreferenceScreen().addPreference(accessPoint);
458                    }
459                }
460                break;
461
462            case WifiManager.WIFI_STATE_ENABLING:
463                getPreferenceScreen().removeAll();
464                break;
465
466            case WifiManager.WIFI_STATE_DISABLING:
467                addMessagePreference(R.string.wifi_stopping);
468                break;
469
470            case WifiManager.WIFI_STATE_DISABLED:
471                addMessagePreference(R.string.wifi_empty_list_wifi_off);
472                break;
473        }
474    }
475
476    private void addMessagePreference(int messageId) {
477        if (mEmptyView != null) mEmptyView.setText(messageId);
478        getPreferenceScreen().removeAll();
479    }
480
481    private Collection<AccessPoint> constructAccessPoints() {
482        Collection<AccessPoint> accessPoints = new ArrayList<AccessPoint>();
483
484        final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
485        if (configs != null) {
486            for (WifiConfiguration config : configs) {
487                AccessPoint accessPoint = new AccessPoint(getActivity(), config);
488                accessPoint.update(mLastInfo, mLastState);
489                accessPoints.add(accessPoint);
490            }
491        }
492
493        final List<ScanResult> results = mWifiManager.getScanResults();
494        if (results != null) {
495            for (ScanResult result : results) {
496                // Ignore hidden and ad-hoc networks.
497                if (result.SSID == null || result.SSID.length() == 0 ||
498                        result.capabilities.contains("[IBSS]")) {
499                    continue;
500                }
501
502                boolean found = false;
503                for (AccessPoint accessPoint : accessPoints) {
504                    if (accessPoint.update(result)) {
505                        found = true;
506                        break;
507                    }
508                }
509                if (!found) {
510                    accessPoints.add(new AccessPoint(getActivity(), result));
511                }
512            }
513        }
514
515        return accessPoints;
516    }
517
518    private void handleEvent(Context context, Intent intent) {
519        String action = intent.getAction();
520        if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
521            updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
522                    WifiManager.WIFI_STATE_UNKNOWN));
523        } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action) ||
524                WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action) ||
525                WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) {
526                updateAccessPoints();
527        } else if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)) {
528            //Ignore supplicant state changes when network is connected
529            //TODO: we should deprecate SUPPLICANT_STATE_CHANGED_ACTION and
530            //introduce a broadcast that combines the supplicant and network
531            //network state change events so the apps dont have to worry about
532            //ignoring supplicant state change when network is connected
533            //to get more fine grained information.
534            if (!mConnected.get()) {
535                updateConnectionState(WifiInfo.getDetailedStateOf((SupplicantState)
536                        intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE)));
537            }
538
539            if (mInXlSetupWizard) {
540                ((WifiSettingsForSetupWizardXL)getActivity()).onSupplicantStateChanged(intent);
541            }
542        } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
543            NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(
544                    WifiManager.EXTRA_NETWORK_INFO);
545            mConnected.set(info.isConnected());
546            changeNextButtonState(info.isConnected());
547            updateAccessPoints();
548            updateConnectionState(info.getDetailedState());
549        } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {
550            updateConnectionState(null);
551        } else if (WifiManager.ERROR_ACTION.equals(action)) {
552            int errorCode = intent.getIntExtra(WifiManager.EXTRA_ERROR_CODE, 0);
553            switch (errorCode) {
554                case WifiManager.WPS_OVERLAP_ERROR:
555                    Toast.makeText(context, R.string.wifi_wps_overlap_error,
556                            Toast.LENGTH_SHORT).show();
557                    break;
558            }
559        }
560    }
561
562    private void updateConnectionState(DetailedState state) {
563        /* sticky broadcasts can call this when wifi is disabled */
564        if (!mWifiManager.isWifiEnabled()) {
565            mScanner.pause();
566            return;
567        }
568
569        if (state == DetailedState.OBTAINING_IPADDR) {
570            mScanner.pause();
571        } else {
572            mScanner.resume();
573        }
574
575        mLastInfo = mWifiManager.getConnectionInfo();
576        if (state != null) {
577            mLastState = state;
578        }
579
580        for (int i = getPreferenceScreen().getPreferenceCount() - 1; i >= 0; --i) {
581            // Maybe there's a WifiConfigPreference
582            Preference preference = getPreferenceScreen().getPreference(i);
583            if (preference instanceof AccessPoint) {
584                final AccessPoint accessPoint = (AccessPoint) preference;
585                accessPoint.update(mLastInfo, mLastState);
586            }
587        }
588
589        if (mInXlSetupWizard) {
590            ((WifiSettingsForSetupWizardXL)getActivity()).updateConnectionState(mLastState);
591        }
592    }
593
594    private void updateWifiState(int state) {
595        getActivity().invalidateOptionsMenu();
596
597        switch (state) {
598            case WifiManager.WIFI_STATE_ENABLED:
599                mScanner.resume();
600                return; // not break, to avoid the call to pause() below
601
602            case WifiManager.WIFI_STATE_ENABLING:
603                addMessagePreference(R.string.wifi_starting);
604                break;
605
606            case WifiManager.WIFI_STATE_DISABLED:
607                addMessagePreference(R.string.wifi_empty_list_wifi_off);
608                break;
609        }
610
611        mLastInfo = null;
612        mLastState = null;
613        mScanner.pause();
614    }
615
616    private class Scanner extends Handler {
617        private int mRetry = 0;
618
619        void resume() {
620            if (!hasMessages(0)) {
621                sendEmptyMessage(0);
622            }
623        }
624
625        void forceScan() {
626            sendEmptyMessage(0);
627        }
628
629        void pause() {
630            mRetry = 0;
631            removeMessages(0);
632        }
633
634        @Override
635        public void handleMessage(Message message) {
636            if (mWifiManager.startScanActive()) {
637                mRetry = 0;
638            } else if (++mRetry >= 3) {
639                mRetry = 0;
640                Toast.makeText(getActivity(), R.string.wifi_fail_to_scan,
641                        Toast.LENGTH_LONG).show();
642                return;
643            }
644            // Combo scans can take 5-6s to complete. Increase interval to 10s.
645            sendEmptyMessageDelayed(0, 10000);
646        }
647    }
648
649    private class WifiServiceHandler extends Handler {
650
651        @Override
652        public void handleMessage(Message msg) {
653            switch (msg.what) {
654                case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
655                    if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
656                        //AsyncChannel in msg.obj
657                    } else {
658                        //AsyncChannel set up failure, ignore
659                        Log.e(TAG, "Failed to establish AsyncChannel connection");
660                    }
661                    break;
662                case WifiManager.CMD_WPS_COMPLETED:
663                    WpsResult result = (WpsResult) msg.obj;
664                    if (result == null) break;
665                    AlertDialog.Builder dialog = new AlertDialog.Builder(getActivity())
666                        .setTitle(R.string.wifi_wps_setup_title)
667                        .setPositiveButton(android.R.string.ok, null);
668                    switch (result.status) {
669                        case FAILURE:
670                            dialog.setMessage(R.string.wifi_wps_failed);
671                            dialog.show();
672                            break;
673                        case IN_PROGRESS:
674                            dialog.setMessage(R.string.wifi_wps_in_progress);
675                            dialog.show();
676                            break;
677                        default:
678                            if (result.pin != null) {
679                                dialog.setMessage(getResources().getString(
680                                        R.string.wifi_wps_pin_output, result.pin));
681                                dialog.show();
682                            }
683                            break;
684                    }
685                    break;
686                //TODO: more connectivity feedback
687                default:
688                    //Ignore
689                    break;
690            }
691        }
692    }
693
694    /**
695     * Renames/replaces "Next" button when appropriate. "Next" button usually exists in
696     * Wifi setup screens, not in usual wifi settings screen.
697     *
698     * @param connected true when the device is connected to a wifi network.
699     */
700    private void changeNextButtonState(boolean connected) {
701        if (mInXlSetupWizard) {
702            ((WifiSettingsForSetupWizardXL)getActivity()).changeNextButtonState(connected);
703        } else if (mEnableNextOnConnection && hasNextButton()) {
704            getNextButton().setEnabled(connected);
705        }
706    }
707
708    public void onClick(DialogInterface dialogInterface, int button) {
709        if (mInXlSetupWizard) {
710            if (button == WifiDialog.BUTTON_FORGET && mSelectedAccessPoint != null) {
711                forget();
712            } else if (button == WifiDialog.BUTTON_SUBMIT) {
713                ((WifiSettingsForSetupWizardXL)getActivity()).onConnectButtonPressed();
714            }
715        } else {
716            if (button == WifiDialog.BUTTON_FORGET && mSelectedAccessPoint != null) {
717                forget();
718            } else if (button == WifiDialog.BUTTON_SUBMIT) {
719                submit(mDialog.getController());
720            }
721        }
722
723    }
724
725    /* package */ void submit(WifiConfigController configController) {
726        int networkSetup = configController.chosenNetworkSetupMethod();
727        switch(networkSetup) {
728            case WifiConfigController.WPS_PBC:
729            case WifiConfigController.WPS_DISPLAY:
730            case WifiConfigController.WPS_KEYPAD:
731                mWifiManager.startWps(configController.getWpsConfig());
732                break;
733            case WifiConfigController.MANUAL:
734                final WifiConfiguration config = configController.getConfig();
735
736                if (config == null) {
737                    if (mSelectedAccessPoint != null
738                            && !requireKeyStore(mSelectedAccessPoint.getConfig())
739                            && mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
740                        mWifiManager.connectNetwork(mSelectedAccessPoint.networkId);
741                    }
742                } else if (config.networkId != INVALID_NETWORK_ID) {
743                    if (mSelectedAccessPoint != null) {
744                        saveNetwork(config);
745                    }
746                } else {
747                    if (configController.isEdit() || requireKeyStore(config)) {
748                        saveNetwork(config);
749                    } else {
750                        mWifiManager.connectNetwork(config);
751                    }
752                }
753                break;
754        }
755
756        if (mWifiManager.isWifiEnabled()) {
757            mScanner.resume();
758        }
759        updateAccessPoints();
760    }
761
762    private void saveNetwork(WifiConfiguration config) {
763        if (mInXlSetupWizard) {
764            ((WifiSettingsForSetupWizardXL)getActivity()).onSaveNetwork(config);
765        } else {
766            mWifiManager.saveNetwork(config);
767        }
768    }
769
770    /* package */ void forget() {
771        mWifiManager.forgetNetwork(mSelectedAccessPoint.networkId);
772
773        if (mWifiManager.isWifiEnabled()) {
774            mScanner.resume();
775        }
776        updateAccessPoints();
777
778        // We need to rename/replace "Next" button in wifi setup context.
779        changeNextButtonState(false);
780    }
781
782    /**
783     * Refreshes acccess points and ask Wifi module to scan networks again.
784     */
785    /* package */ void refreshAccessPoints() {
786        if (mWifiManager.isWifiEnabled()) {
787            mScanner.resume();
788        }
789
790        getPreferenceScreen().removeAll();
791    }
792
793    /**
794     * Called when "add network" button is pressed.
795     */
796    /* package */ void onAddNetworkPressed() {
797        // No exact access point is selected.
798        mSelectedAccessPoint = null;
799        showConfigUi(null, true);
800    }
801
802    /* package */ int getAccessPointsCount() {
803        final boolean wifiIsEnabled = mWifiManager.isWifiEnabled();
804        if (wifiIsEnabled) {
805            return getPreferenceScreen().getPreferenceCount();
806        } else {
807            return 0;
808        }
809    }
810
811    /**
812     * Requests wifi module to pause wifi scan. May be ignored when the module is disabled.
813     */
814    /* package */ void pauseWifiScan() {
815        if (mWifiManager.isWifiEnabled()) {
816            mScanner.pause();
817        }
818    }
819
820    /**
821     * Requests wifi module to resume wifi scan. May be ignored when the module is disabled.
822     */
823    /* package */ void resumeWifiScan() {
824        if (mWifiManager.isWifiEnabled()) {
825            mScanner.resume();
826        }
827    }
828}
829