Home | History | Annotate | Download | only in wifi
      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 
     17 package com.android.settings.wifi;
     18 
     19 import static android.net.wifi.WifiConfiguration.INVALID_NETWORK_ID;
     20 
     21 import android.app.ActionBar;
     22 import android.app.Activity;
     23 import android.app.AlertDialog;
     24 import android.app.Dialog;
     25 import android.content.BroadcastReceiver;
     26 import android.content.Context;
     27 import android.content.DialogInterface;
     28 import android.content.Intent;
     29 import android.content.IntentFilter;
     30 import android.net.ConnectivityManager;
     31 import android.net.NetworkInfo;
     32 import android.net.NetworkInfo.DetailedState;
     33 import android.net.wifi.ScanResult;
     34 import android.net.wifi.SupplicantState;
     35 import android.net.wifi.WifiConfiguration;
     36 import android.net.wifi.WifiConfiguration.KeyMgmt;
     37 import android.net.wifi.WifiInfo;
     38 import android.net.wifi.WifiManager;
     39 import android.net.wifi.WpsResult;
     40 import android.os.Bundle;
     41 import android.os.Handler;
     42 import android.os.Message;
     43 import android.preference.Preference;
     44 import android.preference.PreferenceActivity;
     45 import android.preference.PreferenceScreen;
     46 import android.security.Credentials;
     47 import android.security.KeyStore;
     48 import android.util.Log;
     49 import android.view.ContextMenu;
     50 import android.view.ContextMenu.ContextMenuInfo;
     51 import android.view.Gravity;
     52 import android.view.Menu;
     53 import android.view.MenuInflater;
     54 import android.view.MenuItem;
     55 import android.view.View;
     56 import android.widget.AdapterView.AdapterContextMenuInfo;
     57 import android.widget.Switch;
     58 import android.widget.TextView;
     59 import android.widget.Toast;
     60 
     61 import com.android.internal.util.AsyncChannel;
     62 import com.android.settings.R;
     63 import com.android.settings.SettingsPreferenceFragment;
     64 
     65 import java.util.ArrayList;
     66 import java.util.Collection;
     67 import java.util.Collections;
     68 import java.util.HashMap;
     69 import java.util.List;
     70 import 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  */
     83 public 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     // Combo scans can take 5-6s to complete - set to 10s.
     96     private static final int WIFI_RESCAN_INTERVAL_MS = 10 * 1000;
     97 
     98     // Instance state keys
     99     private static final String SAVE_DIALOG_EDIT_MODE = "edit_mode";
    100     private static final String SAVE_DIALOG_ACCESS_POINT_STATE = "wifi_ap_state";
    101 
    102     private final IntentFilter mFilter;
    103     private final BroadcastReceiver mReceiver;
    104     private final Scanner mScanner;
    105 
    106     private WifiManager mWifiManager;
    107     private WifiEnabler mWifiEnabler;
    108     // An access point being editted is stored here.
    109     private AccessPoint mSelectedAccessPoint;
    110 
    111     private DetailedState mLastState;
    112     private WifiInfo mLastInfo;
    113 
    114     private AtomicBoolean mConnected = new AtomicBoolean(false);
    115 
    116     private int mKeyStoreNetworkId = INVALID_NETWORK_ID;
    117 
    118     private WifiDialog mDialog;
    119 
    120     private TextView mEmptyView;
    121 
    122     /* Used in Wifi Setup context */
    123 
    124     // this boolean extra specifies whether to disable the Next button when not connected
    125     private static final String EXTRA_ENABLE_NEXT_ON_CONNECT = "wifi_enable_next_on_connect";
    126 
    127     // should Next button only be enabled when we have a connection?
    128     private boolean mEnableNextOnConnection;
    129     private boolean mInXlSetupWizard;
    130 
    131     // Save the dialog details
    132     private boolean mDlgEdit;
    133     private AccessPoint mDlgAccessPoint;
    134     private Bundle mAccessPointSavedState;
    135 
    136     /* End of "used in Wifi Setup context" */
    137 
    138     public WifiSettings() {
    139         mFilter = new IntentFilter();
    140         mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
    141         mFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
    142         mFilter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION);
    143         mFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
    144         mFilter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
    145         mFilter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
    146         mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
    147         mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
    148         mFilter.addAction(WifiManager.ERROR_ACTION);
    149 
    150         mReceiver = new BroadcastReceiver() {
    151             @Override
    152             public void onReceive(Context context, Intent intent) {
    153                 handleEvent(context, intent);
    154             }
    155         };
    156 
    157         mScanner = new Scanner();
    158     }
    159 
    160     @Override
    161     public void onAttach(Activity activity) {
    162         super.onAttach(activity);
    163 
    164         mInXlSetupWizard = (activity instanceof WifiSettingsForSetupWizardXL);
    165     }
    166 
    167     @Override
    168     public void onActivityCreated(Bundle savedInstanceState) {
    169         // We don't call super.onActivityCreated() here, since it assumes we already set up
    170         // Preference (probably in onCreate()), while WifiSettings exceptionally set it up in
    171         // this method.
    172 
    173         mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
    174         mWifiManager.asyncConnect(getActivity(), new WifiServiceHandler());
    175         if (savedInstanceState != null
    176                 && savedInstanceState.containsKey(SAVE_DIALOG_ACCESS_POINT_STATE)) {
    177             mDlgEdit = savedInstanceState.getBoolean(SAVE_DIALOG_EDIT_MODE);
    178             mAccessPointSavedState = savedInstanceState.getBundle(SAVE_DIALOG_ACCESS_POINT_STATE);
    179         }
    180 
    181         final Activity activity = getActivity();
    182         final Intent intent = activity.getIntent();
    183 
    184         // if we're supposed to enable/disable the Next button based on our current connection
    185         // state, start it off in the right state
    186         mEnableNextOnConnection = intent.getBooleanExtra(EXTRA_ENABLE_NEXT_ON_CONNECT, false);
    187 
    188         if (mEnableNextOnConnection) {
    189             if (hasNextButton()) {
    190                 final ConnectivityManager connectivity = (ConnectivityManager)
    191                         getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
    192                 if (connectivity != null) {
    193                     NetworkInfo info = connectivity.getNetworkInfo(
    194                             ConnectivityManager.TYPE_WIFI);
    195                     changeNextButtonState(info.isConnected());
    196                 }
    197             }
    198         }
    199 
    200         if (mInXlSetupWizard) {
    201             addPreferencesFromResource(R.xml.wifi_access_points_for_wifi_setup_xl);
    202         } else {
    203             addPreferencesFromResource(R.xml.wifi_settings);
    204 
    205             Switch actionBarSwitch = new Switch(activity);
    206 
    207             if (activity instanceof PreferenceActivity) {
    208                 PreferenceActivity preferenceActivity = (PreferenceActivity) activity;
    209                 if (preferenceActivity.onIsHidingHeaders() || !preferenceActivity.onIsMultiPane()) {
    210                     final int padding = activity.getResources().getDimensionPixelSize(
    211                             R.dimen.action_bar_switch_padding);
    212                     actionBarSwitch.setPadding(0, 0, padding, 0);
    213                     activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
    214                             ActionBar.DISPLAY_SHOW_CUSTOM);
    215                     activity.getActionBar().setCustomView(actionBarSwitch, new ActionBar.LayoutParams(
    216                             ActionBar.LayoutParams.WRAP_CONTENT,
    217                             ActionBar.LayoutParams.WRAP_CONTENT,
    218                             Gravity.CENTER_VERTICAL | Gravity.RIGHT));
    219                 }
    220             }
    221 
    222             mWifiEnabler = new WifiEnabler(activity, actionBarSwitch);
    223         }
    224 
    225         mEmptyView = (TextView) getView().findViewById(android.R.id.empty);
    226         getListView().setEmptyView(mEmptyView);
    227 
    228         registerForContextMenu(getListView());
    229         setHasOptionsMenu(true);
    230 
    231         // After confirming PreferenceScreen is available, we call super.
    232         super.onActivityCreated(savedInstanceState);
    233     }
    234 
    235     @Override
    236     public void onResume() {
    237         super.onResume();
    238         if (mWifiEnabler != null) {
    239             mWifiEnabler.resume();
    240         }
    241 
    242         getActivity().registerReceiver(mReceiver, mFilter);
    243         if (mKeyStoreNetworkId != INVALID_NETWORK_ID &&
    244                 KeyStore.getInstance().state() == KeyStore.State.UNLOCKED) {
    245             mWifiManager.connectNetwork(mKeyStoreNetworkId);
    246         }
    247         mKeyStoreNetworkId = INVALID_NETWORK_ID;
    248 
    249         updateAccessPoints();
    250     }
    251 
    252     @Override
    253     public void onPause() {
    254         super.onPause();
    255         if (mWifiEnabler != null) {
    256             mWifiEnabler.pause();
    257         }
    258         getActivity().unregisterReceiver(mReceiver);
    259         mScanner.pause();
    260     }
    261 
    262     @Override
    263     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    264         // We don't want menus in Setup Wizard XL.
    265         if (!mInXlSetupWizard) {
    266             final boolean wifiIsEnabled = mWifiManager.isWifiEnabled();
    267             menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.wifi_menu_scan)
    268                     //.setIcon(R.drawable.ic_menu_scan_network)
    269                     .setEnabled(wifiIsEnabled)
    270                     .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
    271             menu.add(Menu.NONE, MENU_ID_ADD_NETWORK, 0, R.string.wifi_add_network)
    272                     .setEnabled(wifiIsEnabled)
    273                     .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
    274             menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.wifi_menu_advanced)
    275                     //.setIcon(android.R.drawable.ic_menu_manage)
    276                     .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
    277         }
    278         super.onCreateOptionsMenu(menu, inflater);
    279     }
    280 
    281     @Override
    282     public void onSaveInstanceState(Bundle outState) {
    283         super.onSaveInstanceState(outState);
    284 
    285         // If the dialog is showing, save its state.
    286         if (mDialog != null && mDialog.isShowing()) {
    287             outState.putBoolean(SAVE_DIALOG_EDIT_MODE, mDlgEdit);
    288             if (mDlgAccessPoint != null) {
    289                 mAccessPointSavedState = new Bundle();
    290                 mDlgAccessPoint.saveWifiState(mAccessPointSavedState);
    291                 outState.putBundle(SAVE_DIALOG_ACCESS_POINT_STATE, mAccessPointSavedState);
    292             }
    293         }
    294     }
    295 
    296     @Override
    297     public boolean onOptionsItemSelected(MenuItem item) {
    298         switch (item.getItemId()) {
    299             case MENU_ID_SCAN:
    300                 if (mWifiManager.isWifiEnabled()) {
    301                     mScanner.forceScan();
    302                 }
    303                 return true;
    304             case MENU_ID_ADD_NETWORK:
    305                 if (mWifiManager.isWifiEnabled()) {
    306                     onAddNetworkPressed();
    307                 }
    308                 return true;
    309             case MENU_ID_ADVANCED:
    310                 if (getActivity() instanceof PreferenceActivity) {
    311                     ((PreferenceActivity) getActivity()).startPreferencePanel(
    312                             AdvancedWifiSettings.class.getCanonicalName(),
    313                             null,
    314                             R.string.wifi_advanced_titlebar, null,
    315                             this, 0);
    316                 } else {
    317                     startFragment(this, AdvancedWifiSettings.class.getCanonicalName(), -1, null);
    318                 }
    319                 return true;
    320         }
    321         return super.onOptionsItemSelected(item);
    322     }
    323 
    324     @Override
    325     public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) {
    326         if (mInXlSetupWizard) {
    327             ((WifiSettingsForSetupWizardXL)getActivity()).onCreateContextMenu(menu, view, info);
    328         } else if (info instanceof AdapterContextMenuInfo) {
    329             Preference preference = (Preference) getListView().getItemAtPosition(
    330                     ((AdapterContextMenuInfo) info).position);
    331 
    332             if (preference instanceof AccessPoint) {
    333                 mSelectedAccessPoint = (AccessPoint) preference;
    334                 menu.setHeaderTitle(mSelectedAccessPoint.ssid);
    335                 if (mSelectedAccessPoint.getLevel() != -1
    336                         && mSelectedAccessPoint.getState() == null) {
    337                     menu.add(Menu.NONE, MENU_ID_CONNECT, 0, R.string.wifi_menu_connect);
    338                 }
    339                 if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
    340                     menu.add(Menu.NONE, MENU_ID_FORGET, 0, R.string.wifi_menu_forget);
    341                     menu.add(Menu.NONE, MENU_ID_MODIFY, 0, R.string.wifi_menu_modify);
    342                 }
    343             }
    344         }
    345     }
    346 
    347     @Override
    348     public boolean onContextItemSelected(MenuItem item) {
    349         if (mSelectedAccessPoint == null) {
    350             return super.onContextItemSelected(item);
    351         }
    352         switch (item.getItemId()) {
    353             case MENU_ID_CONNECT: {
    354                 if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
    355                     if (!requireKeyStore(mSelectedAccessPoint.getConfig())) {
    356                         mWifiManager.connectNetwork(mSelectedAccessPoint.networkId);
    357                     }
    358                 } else if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE) {
    359                     /** Bypass dialog for unsecured networks */
    360                     mSelectedAccessPoint.generateOpenNetworkConfig();
    361                     mWifiManager.connectNetwork(mSelectedAccessPoint.getConfig());
    362                 } else {
    363                     showConfigUi(mSelectedAccessPoint, true);
    364                 }
    365                 return true;
    366             }
    367             case MENU_ID_FORGET: {
    368                 mWifiManager.forgetNetwork(mSelectedAccessPoint.networkId);
    369                 return true;
    370             }
    371             case MENU_ID_MODIFY: {
    372                 showConfigUi(mSelectedAccessPoint, true);
    373                 return true;
    374             }
    375         }
    376         return super.onContextItemSelected(item);
    377     }
    378 
    379     @Override
    380     public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {
    381         if (preference instanceof AccessPoint) {
    382             mSelectedAccessPoint = (AccessPoint) preference;
    383             /** Bypass dialog for unsecured, unsaved networks */
    384             if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE &&
    385                     mSelectedAccessPoint.networkId == INVALID_NETWORK_ID) {
    386                 mSelectedAccessPoint.generateOpenNetworkConfig();
    387                 mWifiManager.connectNetwork(mSelectedAccessPoint.getConfig());
    388             } else {
    389                 showConfigUi(mSelectedAccessPoint, false);
    390             }
    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                 // AccessPoints are automatically sorted with TreeSet.
    458                 final Collection<AccessPoint> accessPoints = constructAccessPoints();
    459                 getPreferenceScreen().removeAll();
    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     /** Returns sorted list of access points */
    490     private List<AccessPoint> constructAccessPoints() {
    491         ArrayList<AccessPoint> accessPoints = new ArrayList<AccessPoint>();
    492         /** Lookup table to more quickly update AccessPoints by only considering objects with the
    493          * correct SSID.  Maps SSID -> List of AccessPoints with the given SSID.  */
    494         Multimap<String, AccessPoint> apMap = new Multimap<String, AccessPoint>();
    495 
    496         final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
    497         if (configs != null) {
    498             for (WifiConfiguration config : configs) {
    499                 AccessPoint accessPoint = new AccessPoint(getActivity(), config);
    500                 accessPoint.update(mLastInfo, mLastState);
    501                 accessPoints.add(accessPoint);
    502                 apMap.put(accessPoint.ssid, accessPoint);
    503             }
    504         }
    505 
    506         final List<ScanResult> results = mWifiManager.getScanResults();
    507         if (results != null) {
    508             for (ScanResult result : results) {
    509                 // Ignore hidden and ad-hoc networks.
    510                 if (result.SSID == null || result.SSID.length() == 0 ||
    511                         result.capabilities.contains("[IBSS]")) {
    512                     continue;
    513                 }
    514 
    515                 boolean found = false;
    516                 for (AccessPoint accessPoint : apMap.getAll(result.SSID)) {
    517                     if (accessPoint.update(result))
    518                         found = true;
    519                 }
    520                 if (!found) {
    521                     AccessPoint accessPoint = new AccessPoint(getActivity(), result);
    522                     accessPoints.add(accessPoint);
    523                     apMap.put(accessPoint.ssid, accessPoint);
    524                 }
    525             }
    526         }
    527 
    528         // Pre-sort accessPoints to speed preference insertion
    529         Collections.sort(accessPoints);
    530         return accessPoints;
    531     }
    532 
    533     /** A restricted multimap for use in constructAccessPoints */
    534     private class Multimap<K,V> {
    535         private HashMap<K,List<V>> store = new HashMap<K,List<V>>();
    536         /** retrieve a non-null list of values with key K */
    537         List<V> getAll(K key) {
    538             List<V> values = store.get(key);
    539             return values != null ? values : Collections.<V>emptyList();
    540         }
    541 
    542         void put(K key, V val) {
    543             List<V> curVals = store.get(key);
    544             if (curVals == null) {
    545                 curVals = new ArrayList<V>(3);
    546                 store.put(key, curVals);
    547             }
    548             curVals.add(val);
    549         }
    550     }
    551 
    552     private void handleEvent(Context context, Intent intent) {
    553         String action = intent.getAction();
    554         if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
    555             updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
    556                     WifiManager.WIFI_STATE_UNKNOWN));
    557         } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action) ||
    558                 WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action) ||
    559                 WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) {
    560                 updateAccessPoints();
    561         } else if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)) {
    562             //Ignore supplicant state changes when network is connected
    563             //TODO: we should deprecate SUPPLICANT_STATE_CHANGED_ACTION and
    564             //introduce a broadcast that combines the supplicant and network
    565             //network state change events so the apps dont have to worry about
    566             //ignoring supplicant state change when network is connected
    567             //to get more fine grained information.
    568             if (!mConnected.get()) {
    569                 updateConnectionState(WifiInfo.getDetailedStateOf((SupplicantState)
    570                         intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE)));
    571             }
    572 
    573             if (mInXlSetupWizard) {
    574                 ((WifiSettingsForSetupWizardXL)getActivity()).onSupplicantStateChanged(intent);
    575             }
    576         } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
    577             NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(
    578                     WifiManager.EXTRA_NETWORK_INFO);
    579             mConnected.set(info.isConnected());
    580             changeNextButtonState(info.isConnected());
    581             updateAccessPoints();
    582             updateConnectionState(info.getDetailedState());
    583         } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {
    584             updateConnectionState(null);
    585         } else if (WifiManager.ERROR_ACTION.equals(action)) {
    586             int errorCode = intent.getIntExtra(WifiManager.EXTRA_ERROR_CODE, 0);
    587             switch (errorCode) {
    588                 case WifiManager.WPS_OVERLAP_ERROR:
    589                     Toast.makeText(context, R.string.wifi_wps_overlap_error,
    590                             Toast.LENGTH_SHORT).show();
    591                     break;
    592             }
    593         }
    594     }
    595 
    596     private void updateConnectionState(DetailedState state) {
    597         /* sticky broadcasts can call this when wifi is disabled */
    598         if (!mWifiManager.isWifiEnabled()) {
    599             mScanner.pause();
    600             return;
    601         }
    602 
    603         if (state == DetailedState.OBTAINING_IPADDR) {
    604             mScanner.pause();
    605         } else {
    606             mScanner.resume();
    607         }
    608 
    609         mLastInfo = mWifiManager.getConnectionInfo();
    610         if (state != null) {
    611             mLastState = state;
    612         }
    613 
    614         for (int i = getPreferenceScreen().getPreferenceCount() - 1; i >= 0; --i) {
    615             // Maybe there's a WifiConfigPreference
    616             Preference preference = getPreferenceScreen().getPreference(i);
    617             if (preference instanceof AccessPoint) {
    618                 final AccessPoint accessPoint = (AccessPoint) preference;
    619                 accessPoint.update(mLastInfo, mLastState);
    620             }
    621         }
    622 
    623         if (mInXlSetupWizard) {
    624             ((WifiSettingsForSetupWizardXL)getActivity()).updateConnectionState(mLastState);
    625         }
    626     }
    627 
    628     private void updateWifiState(int state) {
    629         getActivity().invalidateOptionsMenu();
    630 
    631         switch (state) {
    632             case WifiManager.WIFI_STATE_ENABLED:
    633                 mScanner.resume();
    634                 return; // not break, to avoid the call to pause() below
    635 
    636             case WifiManager.WIFI_STATE_ENABLING:
    637                 addMessagePreference(R.string.wifi_starting);
    638                 break;
    639 
    640             case WifiManager.WIFI_STATE_DISABLED:
    641                 addMessagePreference(R.string.wifi_empty_list_wifi_off);
    642                 break;
    643         }
    644 
    645         mLastInfo = null;
    646         mLastState = null;
    647         mScanner.pause();
    648     }
    649 
    650     private class Scanner extends Handler {
    651         private int mRetry = 0;
    652 
    653         void resume() {
    654             if (!hasMessages(0)) {
    655                 sendEmptyMessage(0);
    656             }
    657         }
    658 
    659         void forceScan() {
    660             removeMessages(0);
    661             sendEmptyMessage(0);
    662         }
    663 
    664         void pause() {
    665             mRetry = 0;
    666             removeMessages(0);
    667         }
    668 
    669         @Override
    670         public void handleMessage(Message message) {
    671             if (mWifiManager.startScanActive()) {
    672                 mRetry = 0;
    673             } else if (++mRetry >= 3) {
    674                 mRetry = 0;
    675                 Toast.makeText(getActivity(), R.string.wifi_fail_to_scan,
    676                         Toast.LENGTH_LONG).show();
    677                 return;
    678             }
    679             sendEmptyMessageDelayed(0, WIFI_RESCAN_INTERVAL_MS);
    680         }
    681     }
    682 
    683     private class WifiServiceHandler extends Handler {
    684 
    685         @Override
    686         public void handleMessage(Message msg) {
    687             switch (msg.what) {
    688                 case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
    689                     if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
    690                         //AsyncChannel in msg.obj
    691                     } else {
    692                         //AsyncChannel set up failure, ignore
    693                         Log.e(TAG, "Failed to establish AsyncChannel connection");
    694                     }
    695                     break;
    696                 case WifiManager.CMD_WPS_COMPLETED:
    697                     WpsResult result = (WpsResult) msg.obj;
    698                     if (result == null) break;
    699                     AlertDialog.Builder dialog = new AlertDialog.Builder(getActivity())
    700                         .setTitle(R.string.wifi_wps_setup_title)
    701                         .setPositiveButton(android.R.string.ok, null);
    702                     switch (result.status) {
    703                         case FAILURE:
    704                             dialog.setMessage(R.string.wifi_wps_failed);
    705                             dialog.show();
    706                             break;
    707                         case IN_PROGRESS:
    708                             dialog.setMessage(R.string.wifi_wps_in_progress);
    709                             dialog.show();
    710                             break;
    711                         default:
    712                             if (result.pin != null) {
    713                                 dialog.setMessage(getResources().getString(
    714                                         R.string.wifi_wps_pin_output, result.pin));
    715                                 dialog.show();
    716                             }
    717                             break;
    718                     }
    719                     break;
    720                 //TODO: more connectivity feedback
    721                 default:
    722                     //Ignore
    723                     break;
    724             }
    725         }
    726     }
    727 
    728     /**
    729      * Renames/replaces "Next" button when appropriate. "Next" button usually exists in
    730      * Wifi setup screens, not in usual wifi settings screen.
    731      *
    732      * @param connected true when the device is connected to a wifi network.
    733      */
    734     private void changeNextButtonState(boolean connected) {
    735         if (mInXlSetupWizard) {
    736             ((WifiSettingsForSetupWizardXL)getActivity()).changeNextButtonState(connected);
    737         } else if (mEnableNextOnConnection && hasNextButton()) {
    738             getNextButton().setEnabled(connected);
    739         }
    740     }
    741 
    742     public void onClick(DialogInterface dialogInterface, int button) {
    743         if (mInXlSetupWizard) {
    744             if (button == WifiDialog.BUTTON_FORGET && mSelectedAccessPoint != null) {
    745                 forget();
    746             } else if (button == WifiDialog.BUTTON_SUBMIT) {
    747                 ((WifiSettingsForSetupWizardXL)getActivity()).onConnectButtonPressed();
    748             }
    749         } else {
    750             if (button == WifiDialog.BUTTON_FORGET && mSelectedAccessPoint != null) {
    751                 forget();
    752             } else if (button == WifiDialog.BUTTON_SUBMIT) {
    753                 submit(mDialog.getController());
    754             }
    755         }
    756 
    757     }
    758 
    759     /* package */ void submit(WifiConfigController configController) {
    760         int networkSetup = configController.chosenNetworkSetupMethod();
    761         switch(networkSetup) {
    762             case WifiConfigController.WPS_PBC:
    763             case WifiConfigController.WPS_DISPLAY:
    764             case WifiConfigController.WPS_KEYPAD:
    765                 mWifiManager.startWps(configController.getWpsConfig());
    766                 break;
    767             case WifiConfigController.MANUAL:
    768                 final WifiConfiguration config = configController.getConfig();
    769 
    770                 if (config == null) {
    771                     if (mSelectedAccessPoint != null
    772                             && !requireKeyStore(mSelectedAccessPoint.getConfig())
    773                             && mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
    774                         mWifiManager.connectNetwork(mSelectedAccessPoint.networkId);
    775                     }
    776                 } else if (config.networkId != INVALID_NETWORK_ID) {
    777                     if (mSelectedAccessPoint != null) {
    778                         saveNetwork(config);
    779                     }
    780                 } else {
    781                     if (configController.isEdit() || requireKeyStore(config)) {
    782                         saveNetwork(config);
    783                     } else {
    784                         mWifiManager.connectNetwork(config);
    785                     }
    786                 }
    787                 break;
    788         }
    789 
    790         if (mWifiManager.isWifiEnabled()) {
    791             mScanner.resume();
    792         }
    793         updateAccessPoints();
    794     }
    795 
    796     private void saveNetwork(WifiConfiguration config) {
    797         if (mInXlSetupWizard) {
    798             ((WifiSettingsForSetupWizardXL)getActivity()).onSaveNetwork(config);
    799         } else {
    800             mWifiManager.saveNetwork(config);
    801         }
    802     }
    803 
    804     /* package */ void forget() {
    805         mWifiManager.forgetNetwork(mSelectedAccessPoint.networkId);
    806 
    807         if (mWifiManager.isWifiEnabled()) {
    808             mScanner.resume();
    809         }
    810         updateAccessPoints();
    811 
    812         // We need to rename/replace "Next" button in wifi setup context.
    813         changeNextButtonState(false);
    814     }
    815 
    816     /**
    817      * Refreshes acccess points and ask Wifi module to scan networks again.
    818      */
    819     /* package */ void refreshAccessPoints() {
    820         if (mWifiManager.isWifiEnabled()) {
    821             mScanner.resume();
    822         }
    823 
    824         getPreferenceScreen().removeAll();
    825     }
    826 
    827     /**
    828      * Called when "add network" button is pressed.
    829      */
    830     /* package */ void onAddNetworkPressed() {
    831         // No exact access point is selected.
    832         mSelectedAccessPoint = null;
    833         showConfigUi(null, true);
    834     }
    835 
    836     /* package */ int getAccessPointsCount() {
    837         final boolean wifiIsEnabled = mWifiManager.isWifiEnabled();
    838         if (wifiIsEnabled) {
    839             return getPreferenceScreen().getPreferenceCount();
    840         } else {
    841             return 0;
    842         }
    843     }
    844 
    845     /**
    846      * Requests wifi module to pause wifi scan. May be ignored when the module is disabled.
    847      */
    848     /* package */ void pauseWifiScan() {
    849         if (mWifiManager.isWifiEnabled()) {
    850             mScanner.pause();
    851         }
    852     }
    853 
    854     /**
    855      * Requests wifi module to resume wifi scan. May be ignored when the module is disabled.
    856      */
    857     /* package */ void resumeWifiScan() {
    858         if (mWifiManager.isWifiEnabled()) {
    859             mScanner.resume();
    860         }
    861     }
    862 }
    863