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 import static android.os.UserManager.DISALLOW_CONFIG_WIFI;
     21 
     22 import android.app.Activity;
     23 import android.app.ActivityManager;
     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.content.SharedPreferences;
     31 import android.content.res.Resources;
     32 import android.content.res.TypedArray;
     33 import android.location.LocationManager;
     34 import android.net.ConnectivityManager;
     35 import android.net.NetworkInfo;
     36 import android.net.NetworkInfo.DetailedState;
     37 import android.net.NetworkScoreManager;
     38 import android.net.NetworkScorerAppManager;
     39 import android.net.NetworkScorerAppManager.NetworkScorerAppData;
     40 import android.net.wifi.ScanResult;
     41 import android.net.wifi.WifiConfiguration;
     42 import android.net.wifi.WifiInfo;
     43 import android.net.wifi.WifiManager;
     44 import android.net.wifi.WpsInfo;
     45 import android.os.Build;
     46 import android.os.Bundle;
     47 import android.os.Handler;
     48 import android.os.Message;
     49 import android.os.UserHandle;
     50 import android.preference.Preference;
     51 import android.preference.PreferenceScreen;
     52 import android.util.Log;
     53 import android.view.ContextMenu;
     54 import android.view.ContextMenu.ContextMenuInfo;
     55 import android.view.LayoutInflater;
     56 import android.view.Menu;
     57 import android.view.MenuInflater;
     58 import android.view.MenuItem;
     59 import android.view.View;
     60 import android.view.View.OnClickListener;
     61 import android.widget.AdapterView.AdapterContextMenuInfo;
     62 import android.widget.Button;
     63 import android.widget.TextView;
     64 import android.widget.Toast;
     65 
     66 import com.android.settings.R;
     67 import com.android.settings.RestrictedSettingsFragment;
     68 import com.android.settings.SettingsActivity;
     69 import com.android.settings.search.BaseSearchIndexProvider;
     70 import com.android.settings.search.Indexable;
     71 import com.android.settings.search.SearchIndexableRaw;
     72 
     73 import java.util.ArrayList;
     74 import java.util.Collection;
     75 import java.util.Collections;
     76 import java.util.HashMap;
     77 import java.util.List;
     78 import java.util.concurrent.atomic.AtomicBoolean;
     79 
     80 /**
     81  * Two types of UI are provided here.
     82  *
     83  * The first is for "usual Settings", appearing as any other Setup fragment.
     84  *
     85  * The second is for Setup Wizard, with a simplified interface that hides the action bar
     86  * and menus.
     87  */
     88 public class WifiSettings extends RestrictedSettingsFragment
     89         implements DialogInterface.OnClickListener, Indexable  {
     90 
     91     private static final String TAG = "WifiSettings";
     92 
     93     private static final int REQUEST_ENABLE_WIFI_ASSISTANT = 1;
     94 
     95     /* package */ static final int MENU_ID_WPS_PBC = Menu.FIRST;
     96     private static final int MENU_ID_WPS_PIN = Menu.FIRST + 1;
     97     private static final int MENU_ID_SAVED_NETWORK = Menu.FIRST + 2;
     98     /* package */ static final int MENU_ID_ADD_NETWORK = Menu.FIRST + 3;
     99     private static final int MENU_ID_ADVANCED = Menu.FIRST + 4;
    100     private static final int MENU_ID_SCAN = Menu.FIRST + 5;
    101     private static final int MENU_ID_CONNECT = Menu.FIRST + 6;
    102     private static final int MENU_ID_FORGET = Menu.FIRST + 7;
    103     private static final int MENU_ID_MODIFY = Menu.FIRST + 8;
    104     private static final int MENU_ID_WRITE_NFC = Menu.FIRST + 9;
    105 
    106     private static final String KEY_ASSISTANT_DISMISS_PLATFORM = "assistant_dismiss_platform";
    107 
    108     public static final int WIFI_DIALOG_ID = 1;
    109     /* package */ static final int WPS_PBC_DIALOG_ID = 2;
    110     private static final int WPS_PIN_DIALOG_ID = 3;
    111     private static final int WRITE_NFC_DIALOG_ID = 6;
    112 
    113     // Combo scans can take 5-6s to complete - set to 10s.
    114     private static final int WIFI_RESCAN_INTERVAL_MS = 10 * 1000;
    115 
    116     // Instance state keys
    117     private static final String SAVE_DIALOG_EDIT_MODE = "edit_mode";
    118     private static final String SAVE_DIALOG_ACCESS_POINT_STATE = "wifi_ap_state";
    119 
    120     private static boolean savedNetworksExist;
    121 
    122     private final IntentFilter mFilter;
    123     private final BroadcastReceiver mReceiver;
    124     private final Scanner mScanner;
    125 
    126     /* package */ WifiManager mWifiManager;
    127     private WifiManager.ActionListener mConnectListener;
    128     private WifiManager.ActionListener mSaveListener;
    129     private WifiManager.ActionListener mForgetListener;
    130 
    131     private WifiEnabler mWifiEnabler;
    132     // An access point being editted is stored here.
    133     private AccessPoint mSelectedAccessPoint;
    134 
    135     private DetailedState mLastState;
    136     private WifiInfo mLastInfo;
    137 
    138     private final AtomicBoolean mConnected = new AtomicBoolean(false);
    139 
    140     private WifiDialog mDialog;
    141     private WriteWifiConfigToNfcDialog mWifiToNfcDialog;
    142 
    143     private TextView mEmptyView;
    144 
    145     // this boolean extra specifies whether to disable the Next button when not connected. Used by
    146     // account creation outside of setup wizard.
    147     private static final String EXTRA_ENABLE_NEXT_ON_CONNECT = "wifi_enable_next_on_connect";
    148 
    149     // should Next button only be enabled when we have a connection?
    150     private boolean mEnableNextOnConnection;
    151 
    152     // Save the dialog details
    153     private boolean mDlgEdit;
    154     private AccessPoint mDlgAccessPoint;
    155     private Bundle mAccessPointSavedState;
    156     private View mWifiAssistantCard;
    157     private NetworkScorerAppData mWifiAssistantApp;
    158 
    159     /** verbose logging flag. this flag is set thru developer debugging options
    160      * and used so as to assist with in-the-field WiFi connectivity debugging  */
    161     public static int mVerboseLogging = 0;
    162 
    163     /* End of "used in Wifi Setup context" */
    164 
    165     /** A restricted multimap for use in constructAccessPoints */
    166     private static class Multimap<K,V> {
    167         private final HashMap<K,List<V>> store = new HashMap<K,List<V>>();
    168         /** retrieve a non-null list of values with key K */
    169         List<V> getAll(K key) {
    170             List<V> values = store.get(key);
    171             return values != null ? values : Collections.<V>emptyList();
    172         }
    173 
    174         void put(K key, V val) {
    175             List<V> curVals = store.get(key);
    176             if (curVals == null) {
    177                 curVals = new ArrayList<V>(3);
    178                 store.put(key, curVals);
    179             }
    180             curVals.add(val);
    181         }
    182     }
    183 
    184     private static class Scanner extends Handler {
    185         private int mRetry = 0;
    186         private WifiSettings mWifiSettings = null;
    187 
    188         Scanner(WifiSettings wifiSettings) {
    189             mWifiSettings = wifiSettings;
    190         }
    191 
    192         void resume() {
    193             if (!hasMessages(0)) {
    194                 sendEmptyMessage(0);
    195             }
    196         }
    197 
    198         void forceScan() {
    199             removeMessages(0);
    200             sendEmptyMessage(0);
    201         }
    202 
    203         void pause() {
    204             mRetry = 0;
    205             removeMessages(0);
    206         }
    207 
    208         @Override
    209         public void handleMessage(Message message) {
    210             if (mWifiSettings.mWifiManager.startScan()) {
    211                 mRetry = 0;
    212             } else if (++mRetry >= 3) {
    213                 mRetry = 0;
    214                 Activity activity = mWifiSettings.getActivity();
    215                 if (activity != null) {
    216                     Toast.makeText(activity, R.string.wifi_fail_to_scan, Toast.LENGTH_LONG).show();
    217                 }
    218                 return;
    219             }
    220             sendEmptyMessageDelayed(0, WIFI_RESCAN_INTERVAL_MS);
    221         }
    222     }
    223 
    224     public WifiSettings() {
    225         super(DISALLOW_CONFIG_WIFI);
    226         mFilter = new IntentFilter();
    227         mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
    228         mFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
    229         mFilter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION);
    230         mFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
    231         mFilter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
    232         mFilter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
    233         mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
    234         mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
    235 
    236         mReceiver = new BroadcastReceiver() {
    237             @Override
    238             public void onReceive(Context context, Intent intent) {
    239                 handleEvent(intent);
    240             }
    241         };
    242 
    243         mScanner = new Scanner(this);
    244     }
    245 
    246     @Override
    247     public void onActivityCreated(Bundle savedInstanceState) {
    248         super.onActivityCreated(savedInstanceState);
    249 
    250         mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
    251 
    252         mConnectListener = new WifiManager.ActionListener() {
    253                                    @Override
    254                                    public void onSuccess() {
    255                                    }
    256                                    @Override
    257                                    public void onFailure(int reason) {
    258                                        Activity activity = getActivity();
    259                                        if (activity != null) {
    260                                            Toast.makeText(activity,
    261                                                 R.string.wifi_failed_connect_message,
    262                                                 Toast.LENGTH_SHORT).show();
    263                                        }
    264                                    }
    265                                };
    266 
    267         mSaveListener = new WifiManager.ActionListener() {
    268                                 @Override
    269                                 public void onSuccess() {
    270                                 }
    271                                 @Override
    272                                 public void onFailure(int reason) {
    273                                     Activity activity = getActivity();
    274                                     if (activity != null) {
    275                                         Toast.makeText(activity,
    276                                             R.string.wifi_failed_save_message,
    277                                             Toast.LENGTH_SHORT).show();
    278                                     }
    279                                 }
    280                             };
    281 
    282         mForgetListener = new WifiManager.ActionListener() {
    283                                    @Override
    284                                    public void onSuccess() {
    285                                    }
    286                                    @Override
    287                                    public void onFailure(int reason) {
    288                                        Activity activity = getActivity();
    289                                        if (activity != null) {
    290                                            Toast.makeText(activity,
    291                                                R.string.wifi_failed_forget_message,
    292                                                Toast.LENGTH_SHORT).show();
    293                                        }
    294                                    }
    295                                };
    296 
    297         if (savedInstanceState != null) {
    298             mDlgEdit = savedInstanceState.getBoolean(SAVE_DIALOG_EDIT_MODE);
    299             if (savedInstanceState.containsKey(SAVE_DIALOG_ACCESS_POINT_STATE)) {
    300                 mAccessPointSavedState =
    301                     savedInstanceState.getBundle(SAVE_DIALOG_ACCESS_POINT_STATE);
    302             }
    303         }
    304 
    305         // if we're supposed to enable/disable the Next button based on our current connection
    306         // state, start it off in the right state
    307         Intent intent = getActivity().getIntent();
    308         mEnableNextOnConnection = intent.getBooleanExtra(EXTRA_ENABLE_NEXT_ON_CONNECT, false);
    309 
    310         if (mEnableNextOnConnection) {
    311             if (hasNextButton()) {
    312                 final ConnectivityManager connectivity = (ConnectivityManager)
    313                         getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
    314                 if (connectivity != null) {
    315                     NetworkInfo info = connectivity.getNetworkInfo(
    316                             ConnectivityManager.TYPE_WIFI);
    317                     changeNextButtonState(info.isConnected());
    318                 }
    319             }
    320         }
    321 
    322         addPreferencesFromResource(R.xml.wifi_settings);
    323 
    324         prepareWifiAssistantCard();
    325 
    326         mEmptyView = initEmptyView();
    327         registerForContextMenu(getListView());
    328         setHasOptionsMenu(true);
    329     }
    330 
    331     @Override
    332     public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
    333         if (requestCode == REQUEST_ENABLE_WIFI_ASSISTANT) {
    334             if (resultCode == Activity.RESULT_OK) {
    335                 disableWifiAssistantCardUntilPlatformUpgrade();
    336                 getListView().removeHeaderView(mWifiAssistantCard);
    337                 mWifiAssistantApp = null;
    338             }
    339         } else {
    340             super.onActivityResult(requestCode, resultCode, resultData);
    341         }
    342     }
    343 
    344     @Override
    345     public void onDestroyView() {
    346         super.onDestroyView();
    347 
    348         if (mWifiEnabler != null) {
    349             mWifiEnabler.teardownSwitchBar();
    350         }
    351     }
    352 
    353     @Override
    354     public void onStart() {
    355         super.onStart();
    356 
    357         // On/off switch is hidden for Setup Wizard (returns null)
    358         mWifiEnabler = createWifiEnabler();
    359     }
    360 
    361     /**
    362      * @return new WifiEnabler or null (as overridden by WifiSettingsForSetupWizard)
    363      */
    364     /* package */ WifiEnabler createWifiEnabler() {
    365         final SettingsActivity activity = (SettingsActivity) getActivity();
    366         return new WifiEnabler(activity, activity.getSwitchBar());
    367     }
    368 
    369     @Override
    370     public void onResume() {
    371         final Activity activity = getActivity();
    372         super.onResume();
    373         if (mWifiEnabler != null) {
    374             mWifiEnabler.resume(activity);
    375         }
    376 
    377         activity.registerReceiver(mReceiver, mFilter);
    378         updateAccessPoints();
    379     }
    380 
    381     @Override
    382     public void onPause() {
    383         super.onPause();
    384         if (mWifiEnabler != null) {
    385             mWifiEnabler.pause();
    386         }
    387 
    388         getActivity().unregisterReceiver(mReceiver);
    389         mScanner.pause();
    390     }
    391 
    392     @Override
    393     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    394         // If the user is not allowed to configure wifi, do not show the menu.
    395         if (isUiRestricted()) return;
    396 
    397         addOptionsMenuItems(menu);
    398         super.onCreateOptionsMenu(menu, inflater);
    399     }
    400 
    401     /**
    402      * @param menu
    403      */
    404     void addOptionsMenuItems(Menu menu) {
    405         final boolean wifiIsEnabled = mWifiManager.isWifiEnabled();
    406         TypedArray ta = getActivity().getTheme().obtainStyledAttributes(
    407                 new int[] {R.attr.ic_menu_add, R.attr.ic_wps});
    408         menu.add(Menu.NONE, MENU_ID_ADD_NETWORK, 0, R.string.wifi_add_network)
    409                 .setIcon(ta.getDrawable(0))
    410                 .setEnabled(wifiIsEnabled)
    411                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
    412         if (savedNetworksExist) {
    413             menu.add(Menu.NONE, MENU_ID_SAVED_NETWORK, 0, R.string.wifi_saved_access_points_label)
    414                     .setIcon(ta.getDrawable(0))
    415                     .setEnabled(wifiIsEnabled)
    416                     .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
    417         }
    418         menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.menu_stats_refresh)
    419                .setEnabled(wifiIsEnabled)
    420                .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
    421         menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.wifi_menu_advanced)
    422                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
    423         ta.recycle();
    424     }
    425 
    426     @Override
    427     public void onSaveInstanceState(Bundle outState) {
    428         super.onSaveInstanceState(outState);
    429 
    430         // If the dialog is showing, save its state.
    431         if (mDialog != null && mDialog.isShowing()) {
    432             outState.putBoolean(SAVE_DIALOG_EDIT_MODE, mDlgEdit);
    433             if (mDlgAccessPoint != null) {
    434                 mAccessPointSavedState = new Bundle();
    435                 mDlgAccessPoint.saveWifiState(mAccessPointSavedState);
    436                 outState.putBundle(SAVE_DIALOG_ACCESS_POINT_STATE, mAccessPointSavedState);
    437             }
    438         }
    439     }
    440 
    441     @Override
    442     public boolean onOptionsItemSelected(MenuItem item) {
    443         // If the user is not allowed to configure wifi, do not handle menu selections.
    444         if (isUiRestricted()) return false;
    445 
    446         switch (item.getItemId()) {
    447             case MENU_ID_WPS_PBC:
    448                 showDialog(WPS_PBC_DIALOG_ID);
    449                 return true;
    450                 /*
    451             case MENU_ID_P2P:
    452                 if (getActivity() instanceof SettingsActivity) {
    453                     ((SettingsActivity) getActivity()).startPreferencePanel(
    454                             WifiP2pSettings.class.getCanonicalName(),
    455                             null,
    456                             R.string.wifi_p2p_settings_title, null,
    457                             this, 0);
    458                 } else {
    459                     startFragment(this, WifiP2pSettings.class.getCanonicalName(),
    460                             R.string.wifi_p2p_settings_title, -1, null);
    461                 }
    462                 return true;
    463                 */
    464             case MENU_ID_WPS_PIN:
    465                 showDialog(WPS_PIN_DIALOG_ID);
    466                 return true;
    467             case MENU_ID_SCAN:
    468                 if (mWifiManager.isWifiEnabled()) {
    469                     mScanner.forceScan();
    470                 }
    471                 return true;
    472             case MENU_ID_ADD_NETWORK:
    473                 if (mWifiManager.isWifiEnabled()) {
    474                     onAddNetworkPressed();
    475                 }
    476                 return true;
    477             case MENU_ID_SAVED_NETWORK:
    478                 if (getActivity() instanceof SettingsActivity) {
    479                     ((SettingsActivity) getActivity()).startPreferencePanel(
    480                             SavedAccessPointsWifiSettings.class.getCanonicalName(), null,
    481                             R.string.wifi_saved_access_points_titlebar, null, this, 0);
    482                 } else {
    483                     startFragment(this, SavedAccessPointsWifiSettings.class.getCanonicalName(),
    484                             R.string.wifi_saved_access_points_titlebar,
    485                             -1 /* Do not request a result */, null);
    486                 }
    487                 return true;
    488             case MENU_ID_ADVANCED:
    489                 if (getActivity() instanceof SettingsActivity) {
    490                     ((SettingsActivity) getActivity()).startPreferencePanel(
    491                             AdvancedWifiSettings.class.getCanonicalName(), null,
    492                             R.string.wifi_advanced_titlebar, null, this, 0);
    493                 } else {
    494                     startFragment(this, AdvancedWifiSettings.class.getCanonicalName(),
    495                             R.string.wifi_advanced_titlebar, -1 /* Do not request a results */,
    496                             null);
    497                 }
    498                 return true;
    499         }
    500         return super.onOptionsItemSelected(item);
    501     }
    502 
    503     @Override
    504     public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) {
    505         if (info instanceof AdapterContextMenuInfo) {
    506             Preference preference = (Preference) getListView().getItemAtPosition(
    507                     ((AdapterContextMenuInfo) info).position);
    508 
    509             if (preference instanceof AccessPoint) {
    510                 mSelectedAccessPoint = (AccessPoint) preference;
    511                 menu.setHeaderTitle(mSelectedAccessPoint.ssid);
    512                 if (mSelectedAccessPoint.getLevel() != -1
    513                         && mSelectedAccessPoint.getState() == null) {
    514                     menu.add(Menu.NONE, MENU_ID_CONNECT, 0, R.string.wifi_menu_connect);
    515                 }
    516                 if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
    517                     if (ActivityManager.getCurrentUser() == UserHandle.USER_OWNER) {
    518                         menu.add(Menu.NONE, MENU_ID_FORGET, 0, R.string.wifi_menu_forget);
    519                     }
    520                     menu.add(Menu.NONE, MENU_ID_MODIFY, 0, R.string.wifi_menu_modify);
    521 
    522                     if (mSelectedAccessPoint.security != AccessPoint.SECURITY_NONE) {
    523                         // Only allow writing of NFC tags for password-protected networks.
    524                         menu.add(Menu.NONE, MENU_ID_WRITE_NFC, 0, R.string.wifi_menu_write_to_nfc);
    525                     }
    526                 }
    527             }
    528         }
    529     }
    530 
    531     @Override
    532     public boolean onContextItemSelected(MenuItem item) {
    533         if (mSelectedAccessPoint == null) {
    534             return super.onContextItemSelected(item);
    535         }
    536         switch (item.getItemId()) {
    537             case MENU_ID_CONNECT: {
    538                 if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
    539                     connect(mSelectedAccessPoint.networkId);
    540                 } else if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE) {
    541                     /** Bypass dialog for unsecured networks */
    542                     mSelectedAccessPoint.generateOpenNetworkConfig();
    543                     connect(mSelectedAccessPoint.getConfig());
    544                 } else {
    545                     showDialog(mSelectedAccessPoint, true);
    546                 }
    547                 return true;
    548             }
    549             case MENU_ID_FORGET: {
    550                 mWifiManager.forget(mSelectedAccessPoint.networkId, mForgetListener);
    551                 return true;
    552             }
    553             case MENU_ID_MODIFY: {
    554                 showDialog(mSelectedAccessPoint, true);
    555                 return true;
    556             }
    557             case MENU_ID_WRITE_NFC:
    558                 showDialog(WRITE_NFC_DIALOG_ID);
    559                 return true;
    560 
    561         }
    562         return super.onContextItemSelected(item);
    563     }
    564 
    565     @Override
    566     public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {
    567         if (preference instanceof AccessPoint) {
    568             mSelectedAccessPoint = (AccessPoint) preference;
    569             /** Bypass dialog for unsecured, unsaved networks */
    570             if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE &&
    571                     mSelectedAccessPoint.networkId == INVALID_NETWORK_ID) {
    572                 mSelectedAccessPoint.generateOpenNetworkConfig();
    573                 if (!savedNetworksExist) {
    574                     savedNetworksExist = true;
    575                     getActivity().invalidateOptionsMenu();
    576                 }
    577                 connect(mSelectedAccessPoint.getConfig());
    578             } else {
    579                 showDialog(mSelectedAccessPoint, false);
    580             }
    581         } else {
    582             return super.onPreferenceTreeClick(screen, preference);
    583         }
    584         return true;
    585     }
    586 
    587     private void showDialog(AccessPoint accessPoint, boolean edit) {
    588         if (mDialog != null) {
    589             removeDialog(WIFI_DIALOG_ID);
    590             mDialog = null;
    591         }
    592 
    593         // Save the access point and edit mode
    594         mDlgAccessPoint = accessPoint;
    595         mDlgEdit = edit;
    596 
    597         showDialog(WIFI_DIALOG_ID);
    598     }
    599 
    600     @Override
    601     public Dialog onCreateDialog(int dialogId) {
    602         switch (dialogId) {
    603             case WIFI_DIALOG_ID:
    604                 AccessPoint ap = mDlgAccessPoint; // For manual launch
    605                 if (ap == null) { // For re-launch from saved state
    606                     if (mAccessPointSavedState != null) {
    607                         ap = new AccessPoint(getActivity(), mAccessPointSavedState);
    608                         // For repeated orientation changes
    609                         mDlgAccessPoint = ap;
    610                         // Reset the saved access point data
    611                         mAccessPointSavedState = null;
    612                     }
    613                 }
    614                 // If it's null, fine, it's for Add Network
    615                 mSelectedAccessPoint = ap;
    616                 mDialog = new WifiDialog(getActivity(), this, ap, mDlgEdit);
    617                 return mDialog;
    618             case WPS_PBC_DIALOG_ID:
    619                 return new WpsDialog(getActivity(), WpsInfo.PBC);
    620             case WPS_PIN_DIALOG_ID:
    621                 return new WpsDialog(getActivity(), WpsInfo.DISPLAY);
    622             case WRITE_NFC_DIALOG_ID:
    623                 if (mSelectedAccessPoint != null) {
    624                     mWifiToNfcDialog = new WriteWifiConfigToNfcDialog(
    625                             getActivity(), mSelectedAccessPoint, mWifiManager);
    626                     return mWifiToNfcDialog;
    627                 }
    628 
    629         }
    630         return super.onCreateDialog(dialogId);
    631     }
    632 
    633     /**
    634      * Shows the latest access points available with supplemental information like
    635      * the strength of network and the security for it.
    636      */
    637     private void updateAccessPoints() {
    638         // Safeguard from some delayed event handling
    639         if (getActivity() == null) return;
    640 
    641         if (isUiRestricted()) {
    642             addMessagePreference(R.string.wifi_empty_list_user_restricted);
    643             return;
    644         }
    645         final int wifiState = mWifiManager.getWifiState();
    646 
    647         //when we update the screen, check if verbose logging has been turned on or off
    648         mVerboseLogging = mWifiManager.getVerboseLoggingLevel();
    649 
    650         switch (wifiState) {
    651             case WifiManager.WIFI_STATE_ENABLED:
    652                 // AccessPoints are automatically sorted with TreeSet.
    653                 final Collection<AccessPoint> accessPoints =
    654                         constructAccessPoints(getActivity(), mWifiManager, mLastInfo, mLastState);
    655                 getPreferenceScreen().removeAll();
    656                 if (accessPoints.size() == 0) {
    657                     addMessagePreference(R.string.wifi_empty_list_wifi_on);
    658                 }
    659 
    660                 getListView().removeHeaderView(mWifiAssistantCard);
    661                 if (mWifiAssistantApp != null) {
    662                     getListView().addHeaderView(mWifiAssistantCard);
    663                 }
    664 
    665                 for (AccessPoint accessPoint : accessPoints) {
    666                     // Ignore access points that are out of range.
    667                     if (accessPoint.getLevel() != -1) {
    668                         getPreferenceScreen().addPreference(accessPoint);
    669                     }
    670                 }
    671                 break;
    672 
    673             case WifiManager.WIFI_STATE_ENABLING:
    674                 getPreferenceScreen().removeAll();
    675                 break;
    676 
    677             case WifiManager.WIFI_STATE_DISABLING:
    678                 addMessagePreference(R.string.wifi_stopping);
    679                 break;
    680 
    681             case WifiManager.WIFI_STATE_DISABLED:
    682                 setOffMessage();
    683                 break;
    684         }
    685     }
    686 
    687     /**
    688      * Returns the Network Scorer for the Wifi Assistant App.
    689      */
    690     public static NetworkScorerAppData getWifiAssistantApp(Context context) {
    691         Collection<NetworkScorerAppData> scorers =
    692                 NetworkScorerAppManager.getAllValidScorers(context);
    693 
    694         if (scorers.isEmpty()) {
    695             return null;
    696         }
    697 
    698         // TODO: b/13780935 - Implement proper scorer selection. Rather than pick the first
    699         // scorer on the system, we should allow the user to select one.
    700         return scorers.iterator().next();
    701     }
    702 
    703     private void prepareWifiAssistantCard() {
    704         if (getActivity() instanceof WifiPickerActivity) {
    705             return;
    706         }
    707 
    708         if (NetworkScorerAppManager.getActiveScorer(getActivity()) != null) {
    709             // A scorer is already enabled; don't show the card.
    710             return;
    711         }
    712 
    713         Collection<NetworkScorerAppData> scorers =
    714                 NetworkScorerAppManager.getAllValidScorers(getActivity());
    715         if (scorers.isEmpty()) {
    716             // No scorers are available to enable; don't show the card.
    717             return;
    718         }
    719 
    720         SharedPreferences sharedPreferences = getPreferenceScreen().getSharedPreferences();
    721         int lastDismissPlatform = sharedPreferences.getInt(KEY_ASSISTANT_DISMISS_PLATFORM, 0);
    722 
    723         if (Build.VERSION.SDK_INT <= lastDismissPlatform) {
    724             // User has dismissed the Wi-Fi assistant card on this SDK release. Suppress the card
    725             // until the next major platform upgrade.
    726             return;
    727         }
    728 
    729         // TODO: b/13780935 - Implement proper scorer selection. Rather than pick the first
    730         // scorer on the system, we should allow the user to select one.
    731         mWifiAssistantApp = scorers.iterator().next();
    732 
    733         if (mWifiAssistantCard == null) {
    734             mWifiAssistantCard = LayoutInflater.from(getActivity())
    735                     .inflate(R.layout.wifi_assistant_card, getListView(), false);
    736             Button setup = (Button) mWifiAssistantCard.findViewById(R.id.setup);
    737             Button noThanks = (Button) mWifiAssistantCard.findViewById(R.id.no_thanks_button);
    738             TextView assistantText =
    739                 (TextView) mWifiAssistantCard.findViewById(R.id.wifi_assistant_text);
    740             assistantText.setText(getResources().getString(
    741                     R.string.wifi_assistant_title_message, mWifiAssistantApp.mScorerName));
    742 
    743             if (setup != null && noThanks != null) {
    744                 setup.setOnClickListener(new OnClickListener() {
    745                     @Override
    746                     public void onClick(View v) {
    747                         Intent intent = new Intent();
    748                         if (mWifiAssistantApp.mConfigurationActivityClassName != null) {
    749                             // App has a custom configuration activity; launch that.
    750                             // This custom activity will be responsible for launching the system
    751                             // dialog.
    752                             intent.setClassName(mWifiAssistantApp.mPackageName,
    753                                     mWifiAssistantApp.mConfigurationActivityClassName);
    754                         } else {
    755                             // Fall back on the system dialog.
    756                             intent.setAction(NetworkScoreManager.ACTION_CHANGE_ACTIVE);
    757                             intent.putExtra(NetworkScoreManager.EXTRA_PACKAGE_NAME,
    758                                     mWifiAssistantApp.mPackageName);
    759                         }
    760                         startActivityForResult(intent, REQUEST_ENABLE_WIFI_ASSISTANT);
    761                     }
    762                 });
    763 
    764                 noThanks.setOnClickListener(new OnClickListener() {
    765                     @Override
    766                     public void onClick(View v) {
    767                         disableWifiAssistantCardUntilPlatformUpgrade();
    768                         getListView().removeHeaderView(mWifiAssistantCard);
    769                         mWifiAssistantApp = null;
    770                     }
    771                 });
    772             }
    773         }
    774     }
    775 
    776     private void disableWifiAssistantCardUntilPlatformUpgrade() {
    777         SharedPreferences sharedPreferences = getPreferenceScreen().getSharedPreferences();
    778         SharedPreferences.Editor editor = sharedPreferences.edit();
    779         editor.putInt(KEY_ASSISTANT_DISMISS_PLATFORM, Build.VERSION.SDK_INT);
    780         editor.apply();
    781     }
    782 
    783     protected TextView initEmptyView() {
    784         TextView emptyView = (TextView) getActivity().findViewById(android.R.id.empty);
    785         getListView().setEmptyView(emptyView);
    786         return emptyView;
    787     }
    788 
    789     private void setOffMessage() {
    790         if (mEmptyView != null) {
    791             mEmptyView.setText(R.string.wifi_empty_list_wifi_off);
    792             if (android.provider.Settings.Global.getInt(getActivity().getContentResolver(),
    793                     android.provider.Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 1) {
    794                 mEmptyView.append("\n\n");
    795                 int resId;
    796                 if (android.provider.Settings.Secure.isLocationProviderEnabled(
    797                         getActivity().getContentResolver(), LocationManager.NETWORK_PROVIDER)) {
    798                     resId = R.string.wifi_scan_notify_text_location_on;
    799                 } else {
    800                     resId = R.string.wifi_scan_notify_text_location_off;
    801                 }
    802                 CharSequence charSeq = getText(resId);
    803                 mEmptyView.append(charSeq);
    804             }
    805         }
    806         getPreferenceScreen().removeAll();
    807     }
    808 
    809     private void addMessagePreference(int messageId) {
    810         if (mEmptyView != null) mEmptyView.setText(messageId);
    811         getPreferenceScreen().removeAll();
    812     }
    813 
    814     /** Returns sorted list of access points */
    815     private static List<AccessPoint> constructAccessPoints(Context context,
    816             WifiManager wifiManager, WifiInfo lastInfo, DetailedState lastState) {
    817         ArrayList<AccessPoint> accessPoints = new ArrayList<AccessPoint>();
    818         /** Lookup table to more quickly update AccessPoints by only considering objects with the
    819          * correct SSID.  Maps SSID -> List of AccessPoints with the given SSID.  */
    820         Multimap<String, AccessPoint> apMap = new Multimap<String, AccessPoint>();
    821 
    822         final List<WifiConfiguration> configs = wifiManager.getConfiguredNetworks();
    823         if (configs != null) {
    824             // Update "Saved Networks" menu option.
    825             if (savedNetworksExist != (configs.size() > 0)) {
    826                 savedNetworksExist = !savedNetworksExist;
    827                 if (context instanceof Activity) {
    828                     ((Activity) context).invalidateOptionsMenu();
    829                 }
    830             }
    831             for (WifiConfiguration config : configs) {
    832                 if (config.selfAdded && config.numAssociation == 0) {
    833                     continue;
    834                 }
    835                 AccessPoint accessPoint = new AccessPoint(context, config);
    836                 if (lastInfo != null && lastState != null) {
    837                     accessPoint.update(lastInfo, lastState);
    838                 }
    839                 accessPoints.add(accessPoint);
    840                 apMap.put(accessPoint.ssid, accessPoint);
    841             }
    842         }
    843 
    844         final List<ScanResult> results = wifiManager.getScanResults();
    845         if (results != null) {
    846             for (ScanResult result : results) {
    847                 // Ignore hidden and ad-hoc networks.
    848                 if (result.SSID == null || result.SSID.length() == 0 ||
    849                         result.capabilities.contains("[IBSS]")) {
    850                     continue;
    851                 }
    852 
    853                 boolean found = false;
    854                 for (AccessPoint accessPoint : apMap.getAll(result.SSID)) {
    855                     if (accessPoint.update(result))
    856                         found = true;
    857                 }
    858                 if (!found) {
    859                     AccessPoint accessPoint = new AccessPoint(context, result);
    860                     accessPoints.add(accessPoint);
    861                     apMap.put(accessPoint.ssid, accessPoint);
    862                 }
    863             }
    864         }
    865 
    866         // Pre-sort accessPoints to speed preference insertion
    867         Collections.sort(accessPoints);
    868         return accessPoints;
    869     }
    870 
    871     private void handleEvent(Intent intent) {
    872         String action = intent.getAction();
    873         if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
    874             updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
    875                     WifiManager.WIFI_STATE_UNKNOWN));
    876         } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action) ||
    877                 WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action) ||
    878                 WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) {
    879                 updateAccessPoints();
    880         } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
    881             NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(
    882                     WifiManager.EXTRA_NETWORK_INFO);
    883             mConnected.set(info.isConnected());
    884             changeNextButtonState(info.isConnected());
    885             updateAccessPoints();
    886             updateConnectionState(info.getDetailedState());
    887         } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {
    888             updateConnectionState(null);
    889         }
    890     }
    891 
    892     private void updateConnectionState(DetailedState state) {
    893         /* sticky broadcasts can call this when wifi is disabled */
    894         if (!mWifiManager.isWifiEnabled()) {
    895             mScanner.pause();
    896             return;
    897         }
    898 
    899         if (state == DetailedState.OBTAINING_IPADDR) {
    900             mScanner.pause();
    901         } else {
    902             mScanner.resume();
    903         }
    904 
    905         mLastInfo = mWifiManager.getConnectionInfo();
    906         if (state != null) {
    907             mLastState = state;
    908         }
    909 
    910         for (int i = getPreferenceScreen().getPreferenceCount() - 1; i >= 0; --i) {
    911             // Maybe there's a WifiConfigPreference
    912             Preference preference = getPreferenceScreen().getPreference(i);
    913             if (preference instanceof AccessPoint) {
    914                 final AccessPoint accessPoint = (AccessPoint) preference;
    915                 accessPoint.update(mLastInfo, mLastState);
    916             }
    917         }
    918     }
    919 
    920     private void updateWifiState(int state) {
    921         Activity activity = getActivity();
    922         if (activity != null) {
    923             activity.invalidateOptionsMenu();
    924         }
    925 
    926         switch (state) {
    927             case WifiManager.WIFI_STATE_ENABLED:
    928                 mScanner.resume();
    929                 return; // not break, to avoid the call to pause() below
    930 
    931             case WifiManager.WIFI_STATE_ENABLING:
    932                 addMessagePreference(R.string.wifi_starting);
    933                 break;
    934 
    935             case WifiManager.WIFI_STATE_DISABLED:
    936                 setOffMessage();
    937                 break;
    938         }
    939 
    940         mLastInfo = null;
    941         mLastState = null;
    942         mScanner.pause();
    943     }
    944 
    945     /**
    946      * Renames/replaces "Next" button when appropriate. "Next" button usually exists in
    947      * Wifi setup screens, not in usual wifi settings screen.
    948      *
    949      * @param enabled true when the device is connected to a wifi network.
    950      */
    951     private void changeNextButtonState(boolean enabled) {
    952         if (mEnableNextOnConnection && hasNextButton()) {
    953             getNextButton().setEnabled(enabled);
    954         }
    955     }
    956 
    957     @Override
    958     public void onClick(DialogInterface dialogInterface, int button) {
    959         if (button == WifiDialog.BUTTON_FORGET && mSelectedAccessPoint != null) {
    960             forget();
    961         } else if (button == WifiDialog.BUTTON_SUBMIT) {
    962             if (mDialog != null) {
    963                 submit(mDialog.getController());
    964             }
    965         }
    966     }
    967 
    968     /* package */ void submit(WifiConfigController configController) {
    969 
    970         final WifiConfiguration config = configController.getConfig();
    971 
    972         if (config == null) {
    973             if (mSelectedAccessPoint != null
    974                     && mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
    975                 connect(mSelectedAccessPoint.networkId);
    976             }
    977         } else if (config.networkId != INVALID_NETWORK_ID) {
    978             if (mSelectedAccessPoint != null) {
    979                 mWifiManager.save(config, mSaveListener);
    980             }
    981         } else {
    982             if (configController.isEdit()) {
    983                 mWifiManager.save(config, mSaveListener);
    984             } else {
    985                 connect(config);
    986             }
    987         }
    988 
    989         if (mWifiManager.isWifiEnabled()) {
    990             mScanner.resume();
    991         }
    992         updateAccessPoints();
    993     }
    994 
    995     /* package */ void forget() {
    996         if (mSelectedAccessPoint.networkId == INVALID_NETWORK_ID) {
    997             // Should not happen, but a monkey seems to trigger it
    998             Log.e(TAG, "Failed to forget invalid network " + mSelectedAccessPoint.getConfig());
    999             return;
   1000         }
   1001 
   1002         mWifiManager.forget(mSelectedAccessPoint.networkId, mForgetListener);
   1003 
   1004         if (mWifiManager.isWifiEnabled()) {
   1005             mScanner.resume();
   1006         }
   1007         updateAccessPoints();
   1008 
   1009         // We need to rename/replace "Next" button in wifi setup context.
   1010         changeNextButtonState(false);
   1011     }
   1012 
   1013     protected void connect(final WifiConfiguration config) {
   1014         mWifiManager.connect(config, mConnectListener);
   1015     }
   1016 
   1017     protected void connect(final int networkId) {
   1018         mWifiManager.connect(networkId, mConnectListener);
   1019     }
   1020 
   1021     /**
   1022      * Refreshes acccess points and ask Wifi module to scan networks again.
   1023      */
   1024     /* package */ void refreshAccessPoints() {
   1025         if (mWifiManager.isWifiEnabled()) {
   1026             mScanner.resume();
   1027         }
   1028 
   1029         getPreferenceScreen().removeAll();
   1030     }
   1031 
   1032     /**
   1033      * Called when "add network" button is pressed.
   1034      */
   1035     /* package */ void onAddNetworkPressed() {
   1036         // No exact access point is selected.
   1037         mSelectedAccessPoint = null;
   1038         showDialog(null, true);
   1039     }
   1040 
   1041     /* package */ int getAccessPointsCount() {
   1042         final boolean wifiIsEnabled = mWifiManager.isWifiEnabled();
   1043         if (wifiIsEnabled) {
   1044             return getPreferenceScreen().getPreferenceCount();
   1045         } else {
   1046             return 0;
   1047         }
   1048     }
   1049 
   1050     /**
   1051      * Requests wifi module to pause wifi scan. May be ignored when the module is disabled.
   1052      */
   1053     /* package */ void pauseWifiScan() {
   1054         if (mWifiManager.isWifiEnabled()) {
   1055             mScanner.pause();
   1056         }
   1057     }
   1058 
   1059     /**
   1060      * Requests wifi module to resume wifi scan. May be ignored when the module is disabled.
   1061      */
   1062     /* package */ void resumeWifiScan() {
   1063         if (mWifiManager.isWifiEnabled()) {
   1064             mScanner.resume();
   1065         }
   1066     }
   1067 
   1068     @Override
   1069     protected int getHelpResource() {
   1070         return R.string.help_url_wifi;
   1071     }
   1072 
   1073     public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
   1074         new BaseSearchIndexProvider() {
   1075             @Override
   1076             public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
   1077                 final List<SearchIndexableRaw> result = new ArrayList<SearchIndexableRaw>();
   1078                 final Resources res = context.getResources();
   1079 
   1080                 // Add fragment title
   1081                 SearchIndexableRaw data = new SearchIndexableRaw(context);
   1082                 data.title = res.getString(R.string.wifi_settings);
   1083                 data.screenTitle = res.getString(R.string.wifi_settings);
   1084                 data.keywords = res.getString(R.string.keywords_wifi);
   1085                 result.add(data);
   1086 
   1087                 // Add available Wi-Fi access points
   1088                 WifiManager wifiManager =
   1089                         (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
   1090                 final Collection<AccessPoint> accessPoints =
   1091                         constructAccessPoints(context, wifiManager, null, null);
   1092                 for (AccessPoint accessPoint : accessPoints) {
   1093                     // We are indexing only the saved Wi-Fi networks.
   1094                     if (accessPoint.getConfig() == null) continue;
   1095                     data = new SearchIndexableRaw(context);
   1096                     data.title = accessPoint.getTitle().toString();
   1097                     data.screenTitle = res.getString(R.string.wifi_settings);
   1098                     data.enabled = enabled;
   1099                     result.add(data);
   1100                 }
   1101 
   1102                 return result;
   1103             }
   1104         };
   1105 }
   1106