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.AlertDialog.Builder;
     25 import android.app.Dialog;
     26 import android.content.BroadcastReceiver;
     27 import android.content.Context;
     28 import android.content.DialogInterface;
     29 import android.content.Intent;
     30 import android.content.IntentFilter;
     31 import android.content.pm.PackageManager;
     32 import android.content.res.Resources;
     33 import android.net.ConnectivityManager;
     34 import android.net.NetworkInfo;
     35 import android.net.NetworkInfo.DetailedState;
     36 import android.net.wifi.ScanResult;
     37 import android.net.wifi.SupplicantState;
     38 import android.net.wifi.WifiConfiguration;
     39 import android.net.wifi.WifiInfo;
     40 import android.net.wifi.WifiManager;
     41 import android.net.wifi.WpsInfo;
     42 import android.os.Bundle;
     43 import android.os.Handler;
     44 import android.os.Message;
     45 import android.preference.Preference;
     46 import android.preference.PreferenceActivity;
     47 import android.preference.PreferenceScreen;
     48 import android.security.Credentials;
     49 import android.security.KeyStore;
     50 import android.telephony.TelephonyManager;
     51 import android.util.AttributeSet;
     52 import android.util.Log;
     53 import android.view.ContextMenu;
     54 import android.view.ContextMenu.ContextMenuInfo;
     55 import android.view.Gravity;
     56 import android.view.LayoutInflater;
     57 import android.view.Menu;
     58 import android.view.MenuInflater;
     59 import android.view.MenuItem;
     60 import android.view.View;
     61 import android.view.View.OnClickListener;
     62 import android.view.ViewGroup;
     63 import android.widget.AdapterView.AdapterContextMenuInfo;
     64 import android.widget.Button;
     65 import android.widget.ImageButton;
     66 import android.widget.PopupMenu;
     67 import android.widget.PopupMenu.OnMenuItemClickListener;
     68 import android.widget.RelativeLayout;
     69 import android.widget.Switch;
     70 import android.widget.TextView;
     71 import android.widget.Toast;
     72 
     73 import com.android.settings.R;
     74 import com.android.settings.SettingsPreferenceFragment;
     75 import com.android.settings.wifi.p2p.WifiP2pSettings;
     76 
     77 import java.util.ArrayList;
     78 import java.util.Collection;
     79 import java.util.Collections;
     80 import java.util.HashMap;
     81 import java.util.List;
     82 import java.util.concurrent.atomic.AtomicBoolean;
     83 
     84 /**
     85  * Two types of UI are provided here.
     86  *
     87  * The first is for "usual Settings", appearing as any other Setup fragment.
     88  *
     89  * The second is for Setup Wizard, with a simplified interface that hides the action bar
     90  * and menus.
     91  */
     92 public class WifiSettings extends SettingsPreferenceFragment
     93         implements DialogInterface.OnClickListener  {
     94     private static final String TAG = "WifiSettings";
     95     private 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_P2P = Menu.FIRST + 2;
     98     private 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 
    105     private static final int WIFI_DIALOG_ID = 1;
    106     private static final int WPS_PBC_DIALOG_ID = 2;
    107     private static final int WPS_PIN_DIALOG_ID = 3;
    108     private static final int WIFI_SKIPPED_DIALOG_ID = 4;
    109     private static final int WIFI_AND_MOBILE_SKIPPED_DIALOG_ID = 5;
    110 
    111     // Combo scans can take 5-6s to complete - set to 10s.
    112     private static final int WIFI_RESCAN_INTERVAL_MS = 10 * 1000;
    113 
    114     // Instance state keys
    115     private static final String SAVE_DIALOG_EDIT_MODE = "edit_mode";
    116     private static final String SAVE_DIALOG_ACCESS_POINT_STATE = "wifi_ap_state";
    117 
    118     private final IntentFilter mFilter;
    119     private final BroadcastReceiver mReceiver;
    120     private final Scanner mScanner;
    121 
    122     private WifiManager mWifiManager;
    123     private WifiManager.ActionListener mConnectListener;
    124     private WifiManager.ActionListener mSaveListener;
    125     private WifiManager.ActionListener mForgetListener;
    126     private boolean mP2pSupported;
    127 
    128 
    129     private WifiEnabler mWifiEnabler;
    130     // An access point being editted is stored here.
    131     private AccessPoint mSelectedAccessPoint;
    132 
    133     private DetailedState mLastState;
    134     private WifiInfo mLastInfo;
    135 
    136     private AtomicBoolean mConnected = new AtomicBoolean(false);
    137 
    138     private int mKeyStoreNetworkId = INVALID_NETWORK_ID;
    139 
    140     private WifiDialog mDialog;
    141 
    142     private TextView mEmptyView;
    143 
    144     /* Used in Wifi Setup context */
    145 
    146     // this boolean extra specifies whether to disable the Next button when not connected
    147     private static final String EXTRA_ENABLE_NEXT_ON_CONNECT = "wifi_enable_next_on_connect";
    148 
    149     // this boolean extra specifies whether to auto finish when connection is established
    150     private static final String EXTRA_AUTO_FINISH_ON_CONNECT = "wifi_auto_finish_on_connect";
    151 
    152     // this boolean extra shows a custom button that we can control
    153     protected static final String EXTRA_SHOW_CUSTOM_BUTTON = "wifi_show_custom_button";
    154 
    155     // show a text regarding data charges when wifi connection is required during setup wizard
    156     protected static final String EXTRA_SHOW_WIFI_REQUIRED_INFO = "wifi_show_wifi_required_info";
    157 
    158     // this boolean extra is set if we are being invoked by the Setup Wizard
    159     private static final String EXTRA_IS_FIRST_RUN = "firstRun";
    160 
    161     // should Next button only be enabled when we have a connection?
    162     private boolean mEnableNextOnConnection;
    163 
    164     // should activity finish once we have a connection?
    165     private boolean mAutoFinishOnConnection;
    166 
    167     // Save the dialog details
    168     private boolean mDlgEdit;
    169     private AccessPoint mDlgAccessPoint;
    170     private Bundle mAccessPointSavedState;
    171 
    172     // the action bar uses a different set of controls for Setup Wizard
    173     private boolean mSetupWizardMode;
    174 
    175     /* End of "used in Wifi Setup context" */
    176 
    177     public WifiSettings() {
    178         mFilter = new IntentFilter();
    179         mFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
    180         mFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
    181         mFilter.addAction(WifiManager.NETWORK_IDS_CHANGED_ACTION);
    182         mFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
    183         mFilter.addAction(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION);
    184         mFilter.addAction(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION);
    185         mFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
    186         mFilter.addAction(WifiManager.RSSI_CHANGED_ACTION);
    187 
    188         mReceiver = new BroadcastReceiver() {
    189             @Override
    190             public void onReceive(Context context, Intent intent) {
    191                 handleEvent(context, intent);
    192             }
    193         };
    194 
    195         mScanner = new Scanner();
    196     }
    197 
    198     @Override
    199     public void onCreate(Bundle icicle) {
    200         // Set this flag early, as it's needed by getHelpResource(), which is called by super
    201         mSetupWizardMode = getActivity().getIntent().getBooleanExtra(EXTRA_IS_FIRST_RUN, false);
    202 
    203         super.onCreate(icicle);
    204     }
    205 
    206     @Override
    207     public View onCreateView(final LayoutInflater inflater, ViewGroup container,
    208             Bundle savedInstanceState) {
    209         if (mSetupWizardMode) {
    210             View view = inflater.inflate(R.layout.setup_preference, container, false);
    211             View other = view.findViewById(R.id.other_network);
    212             other.setOnClickListener(new OnClickListener() {
    213                 @Override
    214                 public void onClick(View v) {
    215                     if (mWifiManager.isWifiEnabled()) {
    216                         onAddNetworkPressed();
    217                     }
    218                 }
    219             });
    220             final ImageButton b = (ImageButton) view.findViewById(R.id.more);
    221             if (b != null) {
    222                 b.setOnClickListener(new OnClickListener() {
    223                     @Override
    224                     public void onClick(View v) {
    225                         if (mWifiManager.isWifiEnabled()) {
    226                             PopupMenu pm = new PopupMenu(inflater.getContext(), b);
    227                             pm.inflate(R.menu.wifi_setup);
    228                             pm.setOnMenuItemClickListener(new OnMenuItemClickListener() {
    229                                 @Override
    230                                 public boolean onMenuItemClick(MenuItem item) {
    231                                     if (R.id.wifi_wps == item.getItemId()) {
    232                                         showDialog(WPS_PBC_DIALOG_ID);
    233                                         return true;
    234                                     }
    235                                     return false;
    236                                 }
    237                             });
    238                             pm.show();
    239                         }
    240                     }
    241                 });
    242             }
    243 
    244             Intent intent = getActivity().getIntent();
    245             if (intent.getBooleanExtra(EXTRA_SHOW_CUSTOM_BUTTON, false)) {
    246                 view.findViewById(R.id.button_bar).setVisibility(View.VISIBLE);
    247                 view.findViewById(R.id.back_button).setVisibility(View.INVISIBLE);
    248                 view.findViewById(R.id.skip_button).setVisibility(View.INVISIBLE);
    249                 view.findViewById(R.id.next_button).setVisibility(View.INVISIBLE);
    250 
    251                 Button customButton = (Button) view.findViewById(R.id.custom_button);
    252                 customButton.setVisibility(View.VISIBLE);
    253                 customButton.setOnClickListener(new OnClickListener() {
    254                     @Override
    255                     public void onClick(View v) {
    256                         if (isPhone() && !hasSimProblem()) {
    257                             showDialog(WIFI_SKIPPED_DIALOG_ID);
    258                         } else {
    259                             showDialog(WIFI_AND_MOBILE_SKIPPED_DIALOG_ID);
    260                         }
    261                     }
    262                 });
    263             }
    264 
    265             if (intent.getBooleanExtra(EXTRA_SHOW_WIFI_REQUIRED_INFO, false)) {
    266                 view.findViewById(R.id.wifi_required_info).setVisibility(View.VISIBLE);
    267             }
    268 
    269             return view;
    270         } else {
    271             return super.onCreateView(inflater, container, savedInstanceState);
    272         }
    273     }
    274 
    275     @Override
    276     public void onActivityCreated(Bundle savedInstanceState) {
    277         super.onActivityCreated(savedInstanceState);
    278 
    279         mP2pSupported = getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT);
    280         mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
    281 
    282         mConnectListener = new WifiManager.ActionListener() {
    283                                    public void onSuccess() {
    284                                    }
    285                                    public void onFailure(int reason) {
    286                                        Activity activity = getActivity();
    287                                        if (activity != null) {
    288                                            Toast.makeText(activity,
    289                                                 R.string.wifi_failed_connect_message,
    290                                                 Toast.LENGTH_SHORT).show();
    291                                        }
    292                                    }
    293                                };
    294 
    295         mSaveListener = new WifiManager.ActionListener() {
    296                                 public void onSuccess() {
    297                                 }
    298                                 public void onFailure(int reason) {
    299                                     Activity activity = getActivity();
    300                                     if (activity != null) {
    301                                         Toast.makeText(activity,
    302                                             R.string.wifi_failed_save_message,
    303                                             Toast.LENGTH_SHORT).show();
    304                                     }
    305                                 }
    306                             };
    307 
    308         mForgetListener = new WifiManager.ActionListener() {
    309                                    public void onSuccess() {
    310                                    }
    311                                    public void onFailure(int reason) {
    312                                        Activity activity = getActivity();
    313                                        if (activity != null) {
    314                                            Toast.makeText(activity,
    315                                                R.string.wifi_failed_forget_message,
    316                                                Toast.LENGTH_SHORT).show();
    317                                        }
    318                                    }
    319                                };
    320 
    321         if (savedInstanceState != null
    322                 && savedInstanceState.containsKey(SAVE_DIALOG_ACCESS_POINT_STATE)) {
    323             mDlgEdit = savedInstanceState.getBoolean(SAVE_DIALOG_EDIT_MODE);
    324             mAccessPointSavedState = savedInstanceState.getBundle(SAVE_DIALOG_ACCESS_POINT_STATE);
    325         }
    326 
    327         final Activity activity = getActivity();
    328         final Intent intent = activity.getIntent();
    329 
    330         // first if we're supposed to finish once we have a connection
    331         mAutoFinishOnConnection = intent.getBooleanExtra(EXTRA_AUTO_FINISH_ON_CONNECT, false);
    332 
    333         if (mAutoFinishOnConnection) {
    334             // Hide the next button
    335             if (hasNextButton()) {
    336                 getNextButton().setVisibility(View.GONE);
    337             }
    338 
    339             final ConnectivityManager connectivity = (ConnectivityManager)
    340                     activity.getSystemService(Context.CONNECTIVITY_SERVICE);
    341             if (connectivity != null
    342                     && connectivity.getNetworkInfo(ConnectivityManager.TYPE_WIFI).isConnected()) {
    343                 activity.setResult(Activity.RESULT_OK);
    344                 activity.finish();
    345                 return;
    346             }
    347         }
    348 
    349         // if we're supposed to enable/disable the Next button based on our current connection
    350         // state, start it off in the right state
    351         mEnableNextOnConnection = intent.getBooleanExtra(EXTRA_ENABLE_NEXT_ON_CONNECT, false);
    352 
    353         if (mEnableNextOnConnection) {
    354             if (hasNextButton()) {
    355                 final ConnectivityManager connectivity = (ConnectivityManager)
    356                         activity.getSystemService(Context.CONNECTIVITY_SERVICE);
    357                 if (connectivity != null) {
    358                     NetworkInfo info = connectivity.getNetworkInfo(
    359                             ConnectivityManager.TYPE_WIFI);
    360                     changeNextButtonState(info.isConnected());
    361                 }
    362             }
    363         }
    364 
    365         addPreferencesFromResource(R.xml.wifi_settings);
    366 
    367         if (mSetupWizardMode) {
    368             getView().setSystemUiVisibility(
    369                     View.STATUS_BAR_DISABLE_BACK |
    370                     View.STATUS_BAR_DISABLE_HOME |
    371                     View.STATUS_BAR_DISABLE_RECENT |
    372                     View.STATUS_BAR_DISABLE_NOTIFICATION_ALERTS |
    373                     View.STATUS_BAR_DISABLE_CLOCK);
    374         }
    375 
    376         // On/off switch is hidden for Setup Wizard
    377         if (!mSetupWizardMode) {
    378             Switch actionBarSwitch = new Switch(activity);
    379 
    380             if (activity instanceof PreferenceActivity) {
    381                 PreferenceActivity preferenceActivity = (PreferenceActivity) activity;
    382                 if (preferenceActivity.onIsHidingHeaders() || !preferenceActivity.onIsMultiPane()) {
    383                     final int padding = activity.getResources().getDimensionPixelSize(
    384                             R.dimen.action_bar_switch_padding);
    385                     actionBarSwitch.setPadding(0, 0, padding, 0);
    386                     activity.getActionBar().setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM,
    387                             ActionBar.DISPLAY_SHOW_CUSTOM);
    388                     activity.getActionBar().setCustomView(actionBarSwitch, new ActionBar.LayoutParams(
    389                             ActionBar.LayoutParams.WRAP_CONTENT,
    390                             ActionBar.LayoutParams.WRAP_CONTENT,
    391                             Gravity.CENTER_VERTICAL | Gravity.END));
    392                 }
    393             }
    394 
    395             mWifiEnabler = new WifiEnabler(activity, actionBarSwitch);
    396         }
    397 
    398         mEmptyView = (TextView) getView().findViewById(android.R.id.empty);
    399         getListView().setEmptyView(mEmptyView);
    400 
    401         if (!mSetupWizardMode) {
    402             registerForContextMenu(getListView());
    403         }
    404         setHasOptionsMenu(true);
    405     }
    406 
    407     @Override
    408     public void onResume() {
    409         super.onResume();
    410         if (mWifiEnabler != null) {
    411             mWifiEnabler.resume();
    412         }
    413 
    414         getActivity().registerReceiver(mReceiver, mFilter);
    415         if (mKeyStoreNetworkId != INVALID_NETWORK_ID &&
    416                 KeyStore.getInstance().state() == KeyStore.State.UNLOCKED) {
    417             mWifiManager.connect(mKeyStoreNetworkId, mConnectListener);
    418         }
    419         mKeyStoreNetworkId = INVALID_NETWORK_ID;
    420 
    421         updateAccessPoints();
    422     }
    423 
    424     @Override
    425     public void onPause() {
    426         super.onPause();
    427         if (mWifiEnabler != null) {
    428             mWifiEnabler.pause();
    429         }
    430         getActivity().unregisterReceiver(mReceiver);
    431         mScanner.pause();
    432     }
    433 
    434     @Override
    435     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    436         final boolean wifiIsEnabled = mWifiManager.isWifiEnabled();
    437         if (mSetupWizardMode) {
    438             // FIXME: add setIcon() when graphics are available
    439             menu.add(Menu.NONE, MENU_ID_WPS_PBC, 0, R.string.wifi_menu_wps_pbc)
    440                     .setIcon(R.drawable.ic_wps)
    441                     .setEnabled(wifiIsEnabled)
    442                     .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
    443             menu.add(Menu.NONE, MENU_ID_ADD_NETWORK, 0, R.string.wifi_add_network)
    444                     .setEnabled(wifiIsEnabled)
    445                     .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
    446         } else {
    447             menu.add(Menu.NONE, MENU_ID_WPS_PBC, 0, R.string.wifi_menu_wps_pbc)
    448                     .setIcon(R.drawable.ic_wps)
    449                     .setEnabled(wifiIsEnabled)
    450                     .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
    451             menu.add(Menu.NONE, MENU_ID_ADD_NETWORK, 0, R.string.wifi_add_network)
    452                     .setIcon(R.drawable.ic_menu_add)
    453                     .setEnabled(wifiIsEnabled)
    454                     .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
    455             menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.wifi_menu_scan)
    456                     //.setIcon(R.drawable.ic_menu_scan_network)
    457                     .setEnabled(wifiIsEnabled)
    458                     .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
    459             menu.add(Menu.NONE, MENU_ID_WPS_PIN, 0, R.string.wifi_menu_wps_pin)
    460                     .setEnabled(wifiIsEnabled)
    461                     .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
    462             if (mP2pSupported) {
    463                 menu.add(Menu.NONE, MENU_ID_P2P, 0, R.string.wifi_menu_p2p)
    464                         .setEnabled(wifiIsEnabled)
    465                         .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
    466             }
    467             menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.wifi_menu_advanced)
    468                     //.setIcon(android.R.drawable.ic_menu_manage)
    469                     .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
    470         }
    471         super.onCreateOptionsMenu(menu, inflater);
    472     }
    473 
    474     @Override
    475     public void onSaveInstanceState(Bundle outState) {
    476         super.onSaveInstanceState(outState);
    477 
    478         // If the dialog is showing, save its state.
    479         if (mDialog != null && mDialog.isShowing()) {
    480             outState.putBoolean(SAVE_DIALOG_EDIT_MODE, mDlgEdit);
    481             if (mDlgAccessPoint != null) {
    482                 mAccessPointSavedState = new Bundle();
    483                 mDlgAccessPoint.saveWifiState(mAccessPointSavedState);
    484                 outState.putBundle(SAVE_DIALOG_ACCESS_POINT_STATE, mAccessPointSavedState);
    485             }
    486         }
    487     }
    488 
    489     @Override
    490     public boolean onOptionsItemSelected(MenuItem item) {
    491         switch (item.getItemId()) {
    492             case MENU_ID_WPS_PBC:
    493                 showDialog(WPS_PBC_DIALOG_ID);
    494                 return true;
    495             case MENU_ID_P2P:
    496                 if (getActivity() instanceof PreferenceActivity) {
    497                     ((PreferenceActivity) getActivity()).startPreferencePanel(
    498                             WifiP2pSettings.class.getCanonicalName(),
    499                             null,
    500                             R.string.wifi_p2p_settings_title, null,
    501                             this, 0);
    502                 } else {
    503                     startFragment(this, WifiP2pSettings.class.getCanonicalName(), -1, null);
    504                 }
    505                 return true;
    506             case MENU_ID_WPS_PIN:
    507                 showDialog(WPS_PIN_DIALOG_ID);
    508                 return true;
    509             case MENU_ID_SCAN:
    510                 if (mWifiManager.isWifiEnabled()) {
    511                     mScanner.forceScan();
    512                 }
    513                 return true;
    514             case MENU_ID_ADD_NETWORK:
    515                 if (mWifiManager.isWifiEnabled()) {
    516                     onAddNetworkPressed();
    517                 }
    518                 return true;
    519             case MENU_ID_ADVANCED:
    520                 if (getActivity() instanceof PreferenceActivity) {
    521                     ((PreferenceActivity) getActivity()).startPreferencePanel(
    522                             AdvancedWifiSettings.class.getCanonicalName(),
    523                             null,
    524                             R.string.wifi_advanced_titlebar, null,
    525                             this, 0);
    526                 } else {
    527                     startFragment(this, AdvancedWifiSettings.class.getCanonicalName(), -1, null);
    528                 }
    529                 return true;
    530         }
    531         return super.onOptionsItemSelected(item);
    532     }
    533 
    534     @Override
    535     public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) {
    536         if (info instanceof AdapterContextMenuInfo) {
    537             Preference preference = (Preference) getListView().getItemAtPosition(
    538                     ((AdapterContextMenuInfo) info).position);
    539 
    540             if (preference instanceof AccessPoint) {
    541                 mSelectedAccessPoint = (AccessPoint) preference;
    542                 menu.setHeaderTitle(mSelectedAccessPoint.ssid);
    543                 if (mSelectedAccessPoint.getLevel() != -1
    544                         && mSelectedAccessPoint.getState() == null) {
    545                     menu.add(Menu.NONE, MENU_ID_CONNECT, 0, R.string.wifi_menu_connect);
    546                 }
    547                 if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
    548                     menu.add(Menu.NONE, MENU_ID_FORGET, 0, R.string.wifi_menu_forget);
    549                     menu.add(Menu.NONE, MENU_ID_MODIFY, 0, R.string.wifi_menu_modify);
    550                 }
    551             }
    552         }
    553     }
    554 
    555     @Override
    556     public boolean onContextItemSelected(MenuItem item) {
    557         if (mSelectedAccessPoint == null) {
    558             return super.onContextItemSelected(item);
    559         }
    560         switch (item.getItemId()) {
    561             case MENU_ID_CONNECT: {
    562                 if (mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
    563                     if (!requireKeyStore(mSelectedAccessPoint.getConfig())) {
    564                         mWifiManager.connect(mSelectedAccessPoint.networkId,
    565                                 mConnectListener);
    566                     }
    567                 } else if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE) {
    568                     /** Bypass dialog for unsecured networks */
    569                     mSelectedAccessPoint.generateOpenNetworkConfig();
    570                     mWifiManager.connect(mSelectedAccessPoint.getConfig(),
    571                             mConnectListener);
    572                 } else {
    573                     showDialog(mSelectedAccessPoint, true);
    574                 }
    575                 return true;
    576             }
    577             case MENU_ID_FORGET: {
    578                 mWifiManager.forget(mSelectedAccessPoint.networkId, mForgetListener);
    579                 return true;
    580             }
    581             case MENU_ID_MODIFY: {
    582                 showDialog(mSelectedAccessPoint, true);
    583                 return true;
    584             }
    585         }
    586         return super.onContextItemSelected(item);
    587     }
    588 
    589     @Override
    590     public boolean onPreferenceTreeClick(PreferenceScreen screen, Preference preference) {
    591         if (preference instanceof AccessPoint) {
    592             mSelectedAccessPoint = (AccessPoint) preference;
    593             /** Bypass dialog for unsecured, unsaved networks */
    594             if (mSelectedAccessPoint.security == AccessPoint.SECURITY_NONE &&
    595                     mSelectedAccessPoint.networkId == INVALID_NETWORK_ID) {
    596                 mSelectedAccessPoint.generateOpenNetworkConfig();
    597                 mWifiManager.connect(mSelectedAccessPoint.getConfig(), mConnectListener);
    598             } else {
    599                 showDialog(mSelectedAccessPoint, false);
    600             }
    601         } else {
    602             return super.onPreferenceTreeClick(screen, preference);
    603         }
    604         return true;
    605     }
    606 
    607     private void showDialog(AccessPoint accessPoint, boolean edit) {
    608         if (mDialog != null) {
    609             removeDialog(WIFI_DIALOG_ID);
    610             mDialog = null;
    611         }
    612 
    613         // Save the access point and edit mode
    614         mDlgAccessPoint = accessPoint;
    615         mDlgEdit = edit;
    616 
    617         showDialog(WIFI_DIALOG_ID);
    618     }
    619 
    620     @Override
    621     public Dialog onCreateDialog(int dialogId) {
    622         switch (dialogId) {
    623             case WIFI_DIALOG_ID:
    624                 AccessPoint ap = mDlgAccessPoint; // For manual launch
    625                 if (ap == null) { // For re-launch from saved state
    626                     if (mAccessPointSavedState != null) {
    627                         ap = new AccessPoint(getActivity(), mAccessPointSavedState);
    628                         // For repeated orientation changes
    629                         mDlgAccessPoint = ap;
    630                     }
    631                 }
    632                 // If it's still null, fine, it's for Add Network
    633                 mSelectedAccessPoint = ap;
    634                 mDialog = new WifiDialog(getActivity(), this, ap, mDlgEdit);
    635                 return mDialog;
    636             case WPS_PBC_DIALOG_ID:
    637                 return new WpsDialog(getActivity(), WpsInfo.PBC);
    638             case WPS_PIN_DIALOG_ID:
    639                 return new WpsDialog(getActivity(), WpsInfo.DISPLAY);
    640             case WIFI_SKIPPED_DIALOG_ID:
    641                 return new AlertDialog.Builder(getActivity())
    642                             .setMessage(R.string.wifi_skipped_message)
    643                             .setCancelable(false)
    644                             .setNegativeButton(R.string.wifi_skip_anyway,
    645                                     new DialogInterface.OnClickListener() {
    646                                 @Override
    647                                 public void onClick(DialogInterface dialog, int id) {
    648                                     getActivity().setResult(Activity.RESULT_CANCELED);
    649                                     getActivity().finish();
    650                                 }
    651                             })
    652                             .setPositiveButton(R.string.wifi_dont_skip,
    653                                     new DialogInterface.OnClickListener() {
    654                                 @Override
    655                                 public void onClick(DialogInterface dialog, int id) {
    656                                 }
    657                             })
    658                             .create();
    659             case WIFI_AND_MOBILE_SKIPPED_DIALOG_ID:
    660                 return new AlertDialog.Builder(getActivity())
    661                             .setMessage(R.string.wifi_and_mobile_skipped_message)
    662                             .setCancelable(false)
    663                             .setNegativeButton(R.string.wifi_skip_anyway,
    664                                     new DialogInterface.OnClickListener() {
    665                                 @Override
    666                                 public void onClick(DialogInterface dialog, int id) {
    667                                     getActivity().setResult(Activity.RESULT_CANCELED);
    668                                     getActivity().finish();
    669                                 }
    670                             })
    671                             .setPositiveButton(R.string.wifi_dont_skip,
    672                                     new DialogInterface.OnClickListener() {
    673                                 @Override
    674                                 public void onClick(DialogInterface dialog, int id) {
    675                                 }
    676                             })
    677                             .create();
    678 
    679         }
    680         return super.onCreateDialog(dialogId);
    681     }
    682 
    683     private boolean isPhone() {
    684         final TelephonyManager telephonyManager = (TelephonyManager)this.getSystemService(
    685                 Context.TELEPHONY_SERVICE);
    686         return telephonyManager != null
    687                 && telephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE;
    688     }
    689 
    690     /**
    691     * Return true if there's any SIM related impediment to connectivity.
    692     * Treats Unknown as OK. (Only returns true if we're sure of a SIM problem.)
    693     */
    694    protected boolean hasSimProblem() {
    695        final TelephonyManager telephonyManager = (TelephonyManager)this.getSystemService(
    696                Context.TELEPHONY_SERVICE);
    697        return telephonyManager != null
    698                && telephonyManager.getCurrentPhoneType() == TelephonyManager.PHONE_TYPE_GSM
    699                && telephonyManager.getSimState() != TelephonyManager.SIM_STATE_READY
    700                && telephonyManager.getSimState() != TelephonyManager.SIM_STATE_UNKNOWN;
    701    }
    702 
    703     private boolean requireKeyStore(WifiConfiguration config) {
    704         if (WifiConfigController.requireKeyStore(config) &&
    705                 KeyStore.getInstance().state() != KeyStore.State.UNLOCKED) {
    706             mKeyStoreNetworkId = config.networkId;
    707             Credentials.getInstance().unlock(getActivity());
    708             return true;
    709         }
    710         return false;
    711     }
    712 
    713     /**
    714      * Shows the latest access points available with supplimental information like
    715      * the strength of network and the security for it.
    716      */
    717     private void updateAccessPoints() {
    718         // Safeguard from some delayed event handling
    719         if (getActivity() == null) return;
    720 
    721         final int wifiState = mWifiManager.getWifiState();
    722 
    723         switch (wifiState) {
    724             case WifiManager.WIFI_STATE_ENABLED:
    725                 // AccessPoints are automatically sorted with TreeSet.
    726                 final Collection<AccessPoint> accessPoints = constructAccessPoints();
    727                 getPreferenceScreen().removeAll();
    728                 if(accessPoints.size() == 0) {
    729                     addMessagePreference(R.string.wifi_empty_list_wifi_on);
    730                 }
    731                 for (AccessPoint accessPoint : accessPoints) {
    732                     getPreferenceScreen().addPreference(accessPoint);
    733                 }
    734                 break;
    735 
    736             case WifiManager.WIFI_STATE_ENABLING:
    737                 getPreferenceScreen().removeAll();
    738                 break;
    739 
    740             case WifiManager.WIFI_STATE_DISABLING:
    741                 addMessagePreference(R.string.wifi_stopping);
    742                 break;
    743 
    744             case WifiManager.WIFI_STATE_DISABLED:
    745                 addMessagePreference(R.string.wifi_empty_list_wifi_off);
    746                 break;
    747         }
    748     }
    749 
    750     private void addMessagePreference(int messageId) {
    751         if (mEmptyView != null) mEmptyView.setText(messageId);
    752         getPreferenceScreen().removeAll();
    753     }
    754 
    755     /** Returns sorted list of access points */
    756     private List<AccessPoint> constructAccessPoints() {
    757         ArrayList<AccessPoint> accessPoints = new ArrayList<AccessPoint>();
    758         /** Lookup table to more quickly update AccessPoints by only considering objects with the
    759          * correct SSID.  Maps SSID -> List of AccessPoints with the given SSID.  */
    760         Multimap<String, AccessPoint> apMap = new Multimap<String, AccessPoint>();
    761 
    762         final List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
    763         if (configs != null) {
    764             for (WifiConfiguration config : configs) {
    765                 AccessPoint accessPoint = new AccessPoint(getActivity(), config);
    766                 accessPoint.update(mLastInfo, mLastState);
    767                 accessPoints.add(accessPoint);
    768                 apMap.put(accessPoint.ssid, accessPoint);
    769             }
    770         }
    771 
    772         final List<ScanResult> results = mWifiManager.getScanResults();
    773         if (results != null) {
    774             for (ScanResult result : results) {
    775                 // Ignore hidden and ad-hoc networks.
    776                 if (result.SSID == null || result.SSID.length() == 0 ||
    777                         result.capabilities.contains("[IBSS]")) {
    778                     continue;
    779                 }
    780 
    781                 boolean found = false;
    782                 for (AccessPoint accessPoint : apMap.getAll(result.SSID)) {
    783                     if (accessPoint.update(result))
    784                         found = true;
    785                 }
    786                 if (!found) {
    787                     AccessPoint accessPoint = new AccessPoint(getActivity(), result);
    788                     accessPoints.add(accessPoint);
    789                     apMap.put(accessPoint.ssid, accessPoint);
    790                 }
    791             }
    792         }
    793 
    794         // Pre-sort accessPoints to speed preference insertion
    795         Collections.sort(accessPoints);
    796         return accessPoints;
    797     }
    798 
    799     /** A restricted multimap for use in constructAccessPoints */
    800     private class Multimap<K,V> {
    801         private HashMap<K,List<V>> store = new HashMap<K,List<V>>();
    802         /** retrieve a non-null list of values with key K */
    803         List<V> getAll(K key) {
    804             List<V> values = store.get(key);
    805             return values != null ? values : Collections.<V>emptyList();
    806         }
    807 
    808         void put(K key, V val) {
    809             List<V> curVals = store.get(key);
    810             if (curVals == null) {
    811                 curVals = new ArrayList<V>(3);
    812                 store.put(key, curVals);
    813             }
    814             curVals.add(val);
    815         }
    816     }
    817 
    818     private void handleEvent(Context context, Intent intent) {
    819         String action = intent.getAction();
    820         if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
    821             updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
    822                     WifiManager.WIFI_STATE_UNKNOWN));
    823         } else if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action) ||
    824                 WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION.equals(action) ||
    825                 WifiManager.LINK_CONFIGURATION_CHANGED_ACTION.equals(action)) {
    826                 updateAccessPoints();
    827         } else if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(action)) {
    828             //Ignore supplicant state changes when network is connected
    829             //TODO: we should deprecate SUPPLICANT_STATE_CHANGED_ACTION and
    830             //introduce a broadcast that combines the supplicant and network
    831             //network state change events so the apps dont have to worry about
    832             //ignoring supplicant state change when network is connected
    833             //to get more fine grained information.
    834             SupplicantState state = (SupplicantState) intent.getParcelableExtra(
    835                     WifiManager.EXTRA_NEW_STATE);
    836             if (!mConnected.get() && SupplicantState.isHandshakeState(state)) {
    837                 updateConnectionState(WifiInfo.getDetailedStateOf(state));
    838             }
    839         } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
    840             NetworkInfo info = (NetworkInfo) intent.getParcelableExtra(
    841                     WifiManager.EXTRA_NETWORK_INFO);
    842             mConnected.set(info.isConnected());
    843             changeNextButtonState(info.isConnected());
    844             updateAccessPoints();
    845             updateConnectionState(info.getDetailedState());
    846             if (mAutoFinishOnConnection && info.isConnected()) {
    847                 Activity activity = getActivity();
    848                 if (activity != null) {
    849                     activity.setResult(Activity.RESULT_OK);
    850                     activity.finish();
    851                 }
    852                 return;
    853             }
    854         } else if (WifiManager.RSSI_CHANGED_ACTION.equals(action)) {
    855             updateConnectionState(null);
    856         }
    857     }
    858 
    859     private void updateConnectionState(DetailedState state) {
    860         /* sticky broadcasts can call this when wifi is disabled */
    861         if (!mWifiManager.isWifiEnabled()) {
    862             mScanner.pause();
    863             return;
    864         }
    865 
    866         if (state == DetailedState.OBTAINING_IPADDR) {
    867             mScanner.pause();
    868         } else {
    869             mScanner.resume();
    870         }
    871 
    872         mLastInfo = mWifiManager.getConnectionInfo();
    873         if (state != null) {
    874             mLastState = state;
    875         }
    876 
    877         for (int i = getPreferenceScreen().getPreferenceCount() - 1; i >= 0; --i) {
    878             // Maybe there's a WifiConfigPreference
    879             Preference preference = getPreferenceScreen().getPreference(i);
    880             if (preference instanceof AccessPoint) {
    881                 final AccessPoint accessPoint = (AccessPoint) preference;
    882                 accessPoint.update(mLastInfo, mLastState);
    883             }
    884         }
    885     }
    886 
    887     private void updateWifiState(int state) {
    888         Activity activity = getActivity();
    889         if (activity != null) {
    890             activity.invalidateOptionsMenu();
    891         }
    892 
    893         switch (state) {
    894             case WifiManager.WIFI_STATE_ENABLED:
    895                 mScanner.resume();
    896                 return; // not break, to avoid the call to pause() below
    897 
    898             case WifiManager.WIFI_STATE_ENABLING:
    899                 addMessagePreference(R.string.wifi_starting);
    900                 break;
    901 
    902             case WifiManager.WIFI_STATE_DISABLED:
    903                 addMessagePreference(R.string.wifi_empty_list_wifi_off);
    904                 break;
    905         }
    906 
    907         mLastInfo = null;
    908         mLastState = null;
    909         mScanner.pause();
    910     }
    911 
    912     private class Scanner extends Handler {
    913         private int mRetry = 0;
    914 
    915         void resume() {
    916             if (!hasMessages(0)) {
    917                 sendEmptyMessage(0);
    918             }
    919         }
    920 
    921         void forceScan() {
    922             removeMessages(0);
    923             sendEmptyMessage(0);
    924         }
    925 
    926         void pause() {
    927             mRetry = 0;
    928             removeMessages(0);
    929         }
    930 
    931         @Override
    932         public void handleMessage(Message message) {
    933             if (mWifiManager.startScanActive()) {
    934                 mRetry = 0;
    935             } else if (++mRetry >= 3) {
    936                 mRetry = 0;
    937                 Activity activity = getActivity();
    938                 if (activity != null) {
    939                     Toast.makeText(activity, R.string.wifi_fail_to_scan,
    940                             Toast.LENGTH_LONG).show();
    941                 }
    942                 return;
    943             }
    944             sendEmptyMessageDelayed(0, WIFI_RESCAN_INTERVAL_MS);
    945         }
    946     }
    947 
    948     /**
    949      * Renames/replaces "Next" button when appropriate. "Next" button usually exists in
    950      * Wifi setup screens, not in usual wifi settings screen.
    951      *
    952      * @param connected true when the device is connected to a wifi network.
    953      */
    954     private void changeNextButtonState(boolean connected) {
    955         if (mEnableNextOnConnection && hasNextButton()) {
    956             getNextButton().setEnabled(connected);
    957         }
    958     }
    959 
    960     public void onClick(DialogInterface dialogInterface, int button) {
    961         if (button == WifiDialog.BUTTON_FORGET && mSelectedAccessPoint != null) {
    962             forget();
    963         } else if (button == WifiDialog.BUTTON_SUBMIT) {
    964             submit(mDialog.getController());
    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                     && !requireKeyStore(mSelectedAccessPoint.getConfig())
    975                     && mSelectedAccessPoint.networkId != INVALID_NETWORK_ID) {
    976                 mWifiManager.connect(mSelectedAccessPoint.networkId,
    977                         mConnectListener);
    978             }
    979         } else if (config.networkId != INVALID_NETWORK_ID) {
    980             if (mSelectedAccessPoint != null) {
    981                 mWifiManager.save(config, mSaveListener);
    982             }
    983         } else {
    984             if (configController.isEdit() || requireKeyStore(config)) {
    985                 mWifiManager.save(config, mSaveListener);
    986             } else {
    987                 mWifiManager.connect(config, mConnectListener);
    988             }
    989         }
    990 
    991         if (mWifiManager.isWifiEnabled()) {
    992             mScanner.resume();
    993         }
    994         updateAccessPoints();
    995     }
    996 
    997     /* package */ void forget() {
    998         if (mSelectedAccessPoint.networkId == INVALID_NETWORK_ID) {
    999             // Should not happen, but a monkey seems to triger it
   1000             Log.e(TAG, "Failed to forget invalid network " + mSelectedAccessPoint.getConfig());
   1001             return;
   1002         }
   1003 
   1004         mWifiManager.forget(mSelectedAccessPoint.networkId, mForgetListener);
   1005 
   1006         if (mWifiManager.isWifiEnabled()) {
   1007             mScanner.resume();
   1008         }
   1009         updateAccessPoints();
   1010 
   1011         // We need to rename/replace "Next" button in wifi setup context.
   1012         changeNextButtonState(false);
   1013     }
   1014 
   1015     /**
   1016      * Refreshes acccess points and ask Wifi module to scan networks again.
   1017      */
   1018     /* package */ void refreshAccessPoints() {
   1019         if (mWifiManager.isWifiEnabled()) {
   1020             mScanner.resume();
   1021         }
   1022 
   1023         getPreferenceScreen().removeAll();
   1024     }
   1025 
   1026     /**
   1027      * Called when "add network" button is pressed.
   1028      */
   1029     /* package */ void onAddNetworkPressed() {
   1030         // No exact access point is selected.
   1031         mSelectedAccessPoint = null;
   1032         showDialog(null, true);
   1033     }
   1034 
   1035     /* package */ int getAccessPointsCount() {
   1036         final boolean wifiIsEnabled = mWifiManager.isWifiEnabled();
   1037         if (wifiIsEnabled) {
   1038             return getPreferenceScreen().getPreferenceCount();
   1039         } else {
   1040             return 0;
   1041         }
   1042     }
   1043 
   1044     /**
   1045      * Requests wifi module to pause wifi scan. May be ignored when the module is disabled.
   1046      */
   1047     /* package */ void pauseWifiScan() {
   1048         if (mWifiManager.isWifiEnabled()) {
   1049             mScanner.pause();
   1050         }
   1051     }
   1052 
   1053     /**
   1054      * Requests wifi module to resume wifi scan. May be ignored when the module is disabled.
   1055      */
   1056     /* package */ void resumeWifiScan() {
   1057         if (mWifiManager.isWifiEnabled()) {
   1058             mScanner.resume();
   1059         }
   1060     }
   1061 
   1062     @Override
   1063     protected int getHelpResource() {
   1064         if (mSetupWizardMode) {
   1065             return 0;
   1066         }
   1067         return R.string.help_url_wifi;
   1068     }
   1069 
   1070     /**
   1071      * Used as the outer frame of all setup wizard pages that need to adjust their margins based
   1072      * on the total size of the available display. (e.g. side margins set to 10% of total width.)
   1073      */
   1074     public static class ProportionalOuterFrame extends RelativeLayout {
   1075         public ProportionalOuterFrame(Context context) {
   1076             super(context);
   1077         }
   1078         public ProportionalOuterFrame(Context context, AttributeSet attrs) {
   1079             super(context, attrs);
   1080         }
   1081         public ProportionalOuterFrame(Context context, AttributeSet attrs, int defStyle) {
   1082             super(context, attrs, defStyle);
   1083         }
   1084 
   1085         /**
   1086          * Set our margins and title area height proportionally to the available display size
   1087          */
   1088         @Override
   1089         protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
   1090             int parentWidth = MeasureSpec.getSize(widthMeasureSpec);
   1091             int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
   1092             final Resources resources = getContext().getResources();
   1093             float titleHeight = resources.getFraction(R.dimen.setup_title_height, 1, 1);
   1094             float sideMargin = resources.getFraction(R.dimen.setup_border_width, 1, 1);
   1095             int bottom = resources.getDimensionPixelSize(R.dimen.setup_margin_bottom);
   1096             setPadding(
   1097                     (int) (parentWidth * sideMargin),
   1098                     0,
   1099                     (int) (parentWidth * sideMargin),
   1100                     bottom);
   1101             View title = findViewById(R.id.title_area);
   1102             if (title != null) {
   1103                 title.setMinimumHeight((int) (parentHeight * titleHeight));
   1104             }
   1105             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
   1106         }
   1107     }
   1108 
   1109 }
   1110