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