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 android.app.Activity;
     20 import android.app.Dialog;
     21 import android.app.admin.DevicePolicyManager;
     22 import android.content.BroadcastReceiver;
     23 import android.content.ComponentName;
     24 import android.content.ContentResolver;
     25 import android.content.Context;
     26 import android.content.Intent;
     27 import android.content.IntentFilter;
     28 import android.content.pm.PackageManager;
     29 import android.content.pm.PackageManager.NameNotFoundException;
     30 import android.content.res.Resources;
     31 import android.net.ConnectivityManager;
     32 import android.net.NetworkInfo;
     33 import android.net.NetworkInfo.State;
     34 import android.net.wifi.WifiConfiguration;
     35 import android.net.wifi.WifiManager;
     36 import android.net.wifi.WpsInfo;
     37 import android.nfc.NfcAdapter;
     38 import android.os.Bundle;
     39 import android.os.HandlerThread;
     40 import android.os.Process;
     41 import android.provider.Settings;
     42 import android.support.v7.preference.Preference;
     43 import android.support.v7.preference.PreferenceViewHolder;
     44 import android.text.Spannable;
     45 import android.text.TextUtils;
     46 import android.text.style.TextAppearanceSpan;
     47 import android.util.Log;
     48 import android.view.ContextMenu;
     49 import android.view.ContextMenu.ContextMenuInfo;
     50 import android.view.Menu;
     51 import android.view.MenuInflater;
     52 import android.view.MenuItem;
     53 import android.view.View;
     54 import android.widget.ProgressBar;
     55 import android.widget.TextView;
     56 import android.widget.TextView.BufferType;
     57 import android.widget.Toast;
     58 
     59 import com.android.internal.logging.MetricsLogger;
     60 import com.android.internal.logging.MetricsProto.MetricsEvent;
     61 import com.android.settings.LinkifyUtils;
     62 import com.android.settings.R;
     63 import com.android.settings.RestrictedSettingsFragment;
     64 import com.android.settings.SettingsActivity;
     65 import com.android.settings.dashboard.SummaryLoader;
     66 import com.android.settings.location.ScanningSettings;
     67 import com.android.settings.search.BaseSearchIndexProvider;
     68 import com.android.settings.search.Indexable;
     69 import com.android.settings.search.SearchIndexableRaw;
     70 import com.android.settingslib.RestrictedLockUtils;
     71 import com.android.settingslib.wifi.AccessPoint;
     72 import com.android.settingslib.wifi.AccessPoint.AccessPointListener;
     73 import com.android.settingslib.wifi.AccessPointPreference;
     74 import com.android.settingslib.wifi.WifiStatusTracker;
     75 import com.android.settingslib.wifi.WifiTracker;
     76 
     77 import java.util.ArrayList;
     78 import java.util.Collection;
     79 import java.util.List;
     80 
     81 import static android.os.UserManager.DISALLOW_CONFIG_WIFI;
     82 
     83 /**
     84  * Two types of UI are provided here.
     85  *
     86  * The first is for "usual Settings", appearing as any other Setup fragment.
     87  *
     88  * The second is for Setup Wizard, with a simplified interface that hides the action bar
     89  * and menus.
     90  */
     91 public class WifiSettings extends RestrictedSettingsFragment
     92         implements Indexable, WifiTracker.WifiListener, AccessPointListener,
     93         WifiDialog.WifiDialogListener {
     94 
     95     private static final String TAG = "WifiSettings";
     96 
     97     /* package */ static final int MENU_ID_WPS_PBC = Menu.FIRST;
     98     private static final int MENU_ID_WPS_PIN = Menu.FIRST + 1;
     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     private static final int MENU_ID_CONFIGURE = Menu.FIRST + 10;
    106 
    107     public static final int WIFI_DIALOG_ID = 1;
    108     /* package */ static final int WPS_PBC_DIALOG_ID = 2;
    109     private static final int WPS_PIN_DIALOG_ID = 3;
    110     private static final int WRITE_NFC_DIALOG_ID = 6;
    111 
    112     // Instance state keys
    113     private static final String SAVE_DIALOG_MODE = "dialog_mode";
    114     private static final String SAVE_DIALOG_ACCESS_POINT_STATE = "wifi_ap_state";
    115     private static final String SAVED_WIFI_NFC_DIALOG_STATE = "wifi_nfc_dlg_state";
    116 
    117     private static final String PREF_KEY_EMPTY_WIFI_LIST = "wifi_empty_list";
    118 
    119     protected WifiManager mWifiManager;
    120     private WifiManager.ActionListener mConnectListener;
    121     private WifiManager.ActionListener mSaveListener;
    122     private WifiManager.ActionListener mForgetListener;
    123 
    124     private WifiEnabler mWifiEnabler;
    125     // An access point being editted is stored here.
    126     private AccessPoint mSelectedAccessPoint;
    127 
    128     private WifiDialog mDialog;
    129     private WriteWifiConfigToNfcDialog mWifiToNfcDialog;
    130 
    131     private ProgressBar mProgressHeader;
    132 
    133     // this boolean extra specifies whether to disable the Next button when not connected. Used by
    134     // account creation outside of setup wizard.
    135     private static final String EXTRA_ENABLE_NEXT_ON_CONNECT = "wifi_enable_next_on_connect";
    136     // This string extra specifies a network to open the connect dialog on, so the user can enter
    137     // network credentials.  This is used by quick settings for secured networks.
    138     private static final String EXTRA_START_CONNECT_SSID = "wifi_start_connect_ssid";
    139 
    140     // should Next button only be enabled when we have a connection?
    141     private boolean mEnableNextOnConnection;
    142 
    143     // Save the dialog details
    144     private int mDialogMode;
    145     private AccessPoint mDlgAccessPoint;
    146     private Bundle mAccessPointSavedState;
    147     private Bundle mWifiNfcDialogSavedState;
    148 
    149     private WifiTracker mWifiTracker;
    150     private String mOpenSsid;
    151 
    152     private HandlerThread mBgThread;
    153 
    154     private AccessPointPreference.UserBadgeCache mUserBadgeCache;
    155     private Preference mAddPreference;
    156 
    157     private MenuItem mScanMenuItem;
    158 
    159     /* End of "used in Wifi Setup context" */
    160 
    161     public WifiSettings() {
    162         super(DISALLOW_CONFIG_WIFI);
    163     }
    164 
    165     @Override
    166     public void onViewCreated(View view, Bundle savedInstanceState) {
    167         super.onViewCreated(view, savedInstanceState);
    168         final Activity activity = getActivity();
    169         if (activity != null) {
    170             mProgressHeader = (ProgressBar) setPinnedHeaderView(R.layout.wifi_progress_header);
    171         }
    172     }
    173 
    174     @Override
    175     public void onCreate(Bundle icicle) {
    176         super.onCreate(icicle);
    177         addPreferencesFromResource(R.xml.wifi_settings);
    178         mAddPreference = new Preference(getContext());
    179         mAddPreference.setIcon(R.drawable.ic_menu_add_inset);
    180         mAddPreference.setTitle(R.string.wifi_add_network);
    181 
    182         mUserBadgeCache = new AccessPointPreference.UserBadgeCache(getPackageManager());
    183 
    184         mBgThread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND);
    185         mBgThread.start();
    186     }
    187 
    188     @Override
    189     public void onDestroy() {
    190         mBgThread.quit();
    191         super.onDestroy();
    192     }
    193 
    194     @Override
    195     public void onActivityCreated(Bundle savedInstanceState) {
    196         super.onActivityCreated(savedInstanceState);
    197 
    198         mWifiTracker =
    199                 new WifiTracker(getActivity(), this, mBgThread.getLooper(), true, true, false);
    200         mWifiManager = mWifiTracker.getManager();
    201 
    202         mConnectListener = new WifiManager.ActionListener() {
    203                                    @Override
    204                                    public void onSuccess() {
    205                                    }
    206                                    @Override
    207                                    public void onFailure(int reason) {
    208                                        Activity activity = getActivity();
    209                                        if (activity != null) {
    210                                            Toast.makeText(activity,
    211                                                 R.string.wifi_failed_connect_message,
    212                                                 Toast.LENGTH_SHORT).show();
    213                                        }
    214                                    }
    215                                };
    216 
    217         mSaveListener = new WifiManager.ActionListener() {
    218                                 @Override
    219                                 public void onSuccess() {
    220                                 }
    221                                 @Override
    222                                 public void onFailure(int reason) {
    223                                     Activity activity = getActivity();
    224                                     if (activity != null) {
    225                                         Toast.makeText(activity,
    226                                             R.string.wifi_failed_save_message,
    227                                             Toast.LENGTH_SHORT).show();
    228                                     }
    229                                 }
    230                             };
    231 
    232         mForgetListener = new WifiManager.ActionListener() {
    233                                    @Override
    234                                    public void onSuccess() {
    235                                    }
    236                                    @Override
    237                                    public void onFailure(int reason) {
    238                                        Activity activity = getActivity();
    239                                        if (activity != null) {
    240                                            Toast.makeText(activity,
    241                                                R.string.wifi_failed_forget_message,
    242                                                Toast.LENGTH_SHORT).show();
    243                                        }
    244                                    }
    245                                };
    246 
    247         if (savedInstanceState != null) {
    248             mDialogMode = savedInstanceState.getInt(SAVE_DIALOG_MODE);
    249             if (savedInstanceState.containsKey(SAVE_DIALOG_ACCESS_POINT_STATE)) {
    250                 mAccessPointSavedState =
    251                     savedInstanceState.getBundle(SAVE_DIALOG_ACCESS_POINT_STATE);
    252             }
    253 
    254             if (savedInstanceState.containsKey(SAVED_WIFI_NFC_DIALOG_STATE)) {
    255                 mWifiNfcDialogSavedState =
    256                     savedInstanceState.getBundle(SAVED_WIFI_NFC_DIALOG_STATE);
    257             }
    258         }
    259 
    260         // if we're supposed to enable/disable the Next button based on our current connection
    261         // state, start it off in the right state
    262         Intent intent = getActivity().getIntent();
    263         mEnableNextOnConnection = intent.getBooleanExtra(EXTRA_ENABLE_NEXT_ON_CONNECT, false);
    264 
    265         if (mEnableNextOnConnection) {
    266             if (hasNextButton()) {
    267                 final ConnectivityManager connectivity = (ConnectivityManager)
    268                         getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
    269                 if (connectivity != null) {
    270                     NetworkInfo info = connectivity.getNetworkInfo(
    271                             ConnectivityManager.TYPE_WIFI);
    272                     changeNextButtonState(info.isConnected());
    273                 }
    274             }
    275         }
    276 
    277         registerForContextMenu(getListView());
    278         setHasOptionsMenu(true);
    279 
    280         if (intent.hasExtra(EXTRA_START_CONNECT_SSID)) {
    281             mOpenSsid = intent.getStringExtra(EXTRA_START_CONNECT_SSID);
    282             onAccessPointsChanged();
    283         }
    284     }
    285 
    286     @Override
    287     public void onDestroyView() {
    288         super.onDestroyView();
    289 
    290         if (mWifiEnabler != null) {
    291             mWifiEnabler.teardownSwitchBar();
    292         }
    293     }
    294 
    295     @Override
    296     public void onStart() {
    297         super.onStart();
    298 
    299         // On/off switch is hidden for Setup Wizard (returns null)
    300         mWifiEnabler = createWifiEnabler();
    301     }
    302 
    303     /**
    304      * @return new WifiEnabler or null (as overridden by WifiSettingsForSetupWizard)
    305      */
    306     /* package */ WifiEnabler createWifiEnabler() {
    307         final SettingsActivity activity = (SettingsActivity) getActivity();
    308         return new WifiEnabler(activity, activity.getSwitchBar());
    309     }
    310 
    311     @Override
    312     public void onResume() {
    313         final Activity activity = getActivity();
    314         super.onResume();
    315         removePreference("dummy");
    316         if (mWifiEnabler != null) {
    317             mWifiEnabler.resume(activity);
    318         }
    319 
    320         mWifiTracker.startTracking();
    321         activity.invalidateOptionsMenu();
    322     }
    323 
    324     @Override
    325     public void onPause() {
    326         super.onPause();
    327         if (mWifiEnabler != null) {
    328             mWifiEnabler.pause();
    329         }
    330 
    331         mWifiTracker.stopTracking();
    332     }
    333 
    334     @Override
    335     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    336         // If the user is not allowed to configure wifi, do not show the menu.
    337         if (isUiRestricted()) return;
    338 
    339         addOptionsMenuItems(menu);
    340         super.onCreateOptionsMenu(menu, inflater);
    341     }
    342 
    343     /**
    344      * @param menu
    345      */
    346     void addOptionsMenuItems(Menu menu) {
    347         final boolean wifiIsEnabled = mWifiTracker.isWifiEnabled();
    348         mScanMenuItem = menu.add(Menu.NONE, MENU_ID_SCAN, 0, R.string.menu_stats_refresh);
    349         mScanMenuItem.setEnabled(wifiIsEnabled)
    350                .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
    351         menu.add(Menu.NONE, MENU_ID_ADVANCED, 0, R.string.wifi_menu_advanced)
    352                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
    353         menu.add(Menu.NONE, MENU_ID_CONFIGURE, 0, R.string.wifi_menu_configure)
    354                 .setIcon(R.drawable.ic_settings_24dp)
    355                 .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
    356     }
    357 
    358     @Override
    359     protected int getMetricsCategory() {
    360         return MetricsEvent.WIFI;
    361     }
    362 
    363     @Override
    364     public void onSaveInstanceState(Bundle outState) {
    365         super.onSaveInstanceState(outState);
    366 
    367         // If the dialog is showing, save its state.
    368         if (mDialog != null && mDialog.isShowing()) {
    369             outState.putInt(SAVE_DIALOG_MODE, mDialogMode);
    370             if (mDlgAccessPoint != null) {
    371                 mAccessPointSavedState = new Bundle();
    372                 mDlgAccessPoint.saveWifiState(mAccessPointSavedState);
    373                 outState.putBundle(SAVE_DIALOG_ACCESS_POINT_STATE, mAccessPointSavedState);
    374             }
    375         }
    376 
    377         if (mWifiToNfcDialog != null && mWifiToNfcDialog.isShowing()) {
    378             Bundle savedState = new Bundle();
    379             mWifiToNfcDialog.saveState(savedState);
    380             outState.putBundle(SAVED_WIFI_NFC_DIALOG_STATE, savedState);
    381         }
    382     }
    383 
    384     @Override
    385     public boolean onOptionsItemSelected(MenuItem item) {
    386         // If the user is not allowed to configure wifi, do not handle menu selections.
    387         if (isUiRestricted()) return false;
    388 
    389         switch (item.getItemId()) {
    390             case MENU_ID_WPS_PBC:
    391                 showDialog(WPS_PBC_DIALOG_ID);
    392                 return true;
    393                 /*
    394             case MENU_ID_P2P:
    395                 if (getActivity() instanceof SettingsActivity) {
    396                     ((SettingsActivity) getActivity()).startPreferencePanel(
    397                             WifiP2pSettings.class.getCanonicalName(),
    398                             null,
    399                             R.string.wifi_p2p_settings_title, null,
    400                             this, 0);
    401                 } else {
    402                     startFragment(this, WifiP2pSettings.class.getCanonicalName(),
    403                             R.string.wifi_p2p_settings_title, -1, null);
    404                 }
    405                 return true;
    406                 */
    407             case MENU_ID_WPS_PIN:
    408                 showDialog(WPS_PIN_DIALOG_ID);
    409                 return true;
    410             case MENU_ID_SCAN:
    411                 MetricsLogger.action(getActivity(), MetricsEvent.ACTION_WIFI_FORCE_SCAN);
    412                 mWifiTracker.forceScan();
    413                 return true;
    414             case MENU_ID_ADVANCED:
    415                 if (getActivity() instanceof SettingsActivity) {
    416                     ((SettingsActivity) getActivity()).startPreferencePanel(
    417                             AdvancedWifiSettings.class.getCanonicalName(), null,
    418                             R.string.wifi_advanced_titlebar, null, this, 0);
    419                 } else {
    420                     startFragment(this, AdvancedWifiSettings.class.getCanonicalName(),
    421                             R.string.wifi_advanced_titlebar, -1 /* Do not request a results */,
    422                             null);
    423                 }
    424                 return true;
    425             case MENU_ID_CONFIGURE:
    426                 if (getActivity() instanceof SettingsActivity) {
    427                     ((SettingsActivity) getActivity()).startPreferencePanel(
    428                             ConfigureWifiSettings.class.getCanonicalName(), null,
    429                             R.string.wifi_configure_titlebar, null, this, 0);
    430                 } else {
    431                     startFragment(this, ConfigureWifiSettings.class.getCanonicalName(),
    432                             R.string.wifi_configure_titlebar, -1 /* Do not request a results */,
    433                             null);
    434                 }
    435                 return true;
    436 
    437         }
    438         return super.onOptionsItemSelected(item);
    439     }
    440 
    441     @Override
    442     public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) {
    443             Preference preference = (Preference) view.getTag();
    444 
    445             if (preference instanceof LongPressAccessPointPreference) {
    446                 mSelectedAccessPoint =
    447                         ((LongPressAccessPointPreference) preference).getAccessPoint();
    448                 menu.setHeaderTitle(mSelectedAccessPoint.getSsid());
    449                 if (mSelectedAccessPoint.isConnectable()) {
    450                     menu.add(Menu.NONE, MENU_ID_CONNECT, 0, R.string.wifi_menu_connect);
    451                 }
    452 
    453                 WifiConfiguration config = mSelectedAccessPoint.getConfig();
    454                 // Some configs are ineditable
    455                 if (isEditabilityLockedDown(getActivity(), config)) {
    456                     return;
    457                 }
    458 
    459                 if (mSelectedAccessPoint.isSaved() || mSelectedAccessPoint.isEphemeral()) {
    460                     // Allow forgetting a network if either the network is saved or ephemerally
    461                     // connected. (In the latter case, "forget" blacklists the network so it won't
    462                     // be used again, ephemerally).
    463                     menu.add(Menu.NONE, MENU_ID_FORGET, 0, R.string.wifi_menu_forget);
    464                 }
    465                 if (mSelectedAccessPoint.isSaved()) {
    466                     menu.add(Menu.NONE, MENU_ID_MODIFY, 0, R.string.wifi_menu_modify);
    467                     NfcAdapter nfcAdapter = NfcAdapter.getDefaultAdapter(getActivity());
    468                     if (nfcAdapter != null && nfcAdapter.isEnabled() &&
    469                             mSelectedAccessPoint.getSecurity() != AccessPoint.SECURITY_NONE) {
    470                         // Only allow writing of NFC tags for password-protected networks.
    471                         menu.add(Menu.NONE, MENU_ID_WRITE_NFC, 0, R.string.wifi_menu_write_to_nfc);
    472                     }
    473                 }
    474             }
    475     }
    476 
    477     @Override
    478     public boolean onContextItemSelected(MenuItem item) {
    479         if (mSelectedAccessPoint == null) {
    480             return super.onContextItemSelected(item);
    481         }
    482         switch (item.getItemId()) {
    483             case MENU_ID_CONNECT: {
    484                 if (mSelectedAccessPoint.isSaved()) {
    485                     connect(mSelectedAccessPoint.getConfig());
    486                 } else if (mSelectedAccessPoint.getSecurity() == AccessPoint.SECURITY_NONE) {
    487                     /** Bypass dialog for unsecured networks */
    488                     mSelectedAccessPoint.generateOpenNetworkConfig();
    489                     connect(mSelectedAccessPoint.getConfig());
    490                 } else {
    491                     showDialog(mSelectedAccessPoint, WifiConfigUiBase.MODE_CONNECT);
    492                 }
    493                 return true;
    494             }
    495             case MENU_ID_FORGET: {
    496                 forget();
    497                 return true;
    498             }
    499             case MENU_ID_MODIFY: {
    500                 showDialog(mSelectedAccessPoint, WifiConfigUiBase.MODE_MODIFY);
    501                 return true;
    502             }
    503             case MENU_ID_WRITE_NFC:
    504                 showDialog(WRITE_NFC_DIALOG_ID);
    505                 return true;
    506 
    507         }
    508         return super.onContextItemSelected(item);
    509     }
    510 
    511     @Override
    512     public boolean onPreferenceTreeClick(Preference preference) {
    513         if (preference instanceof LongPressAccessPointPreference) {
    514             mSelectedAccessPoint = ((LongPressAccessPointPreference) preference).getAccessPoint();
    515             if (mSelectedAccessPoint == null) {
    516                 return false;
    517             }
    518             /** Bypass dialog for unsecured, unsaved, and inactive networks */
    519             if (mSelectedAccessPoint.getSecurity() == AccessPoint.SECURITY_NONE &&
    520                     !mSelectedAccessPoint.isSaved() && !mSelectedAccessPoint.isActive()) {
    521                 mSelectedAccessPoint.generateOpenNetworkConfig();
    522                 connect(mSelectedAccessPoint.getConfig());
    523             } else if (mSelectedAccessPoint.isSaved()) {
    524                 showDialog(mSelectedAccessPoint, WifiConfigUiBase.MODE_VIEW);
    525             } else {
    526                 showDialog(mSelectedAccessPoint, WifiConfigUiBase.MODE_CONNECT);
    527             }
    528         } else if (preference == mAddPreference) {
    529             onAddNetworkPressed();
    530         } else {
    531             return super.onPreferenceTreeClick(preference);
    532         }
    533         return true;
    534     }
    535 
    536     private void showDialog(AccessPoint accessPoint, int dialogMode) {
    537         if (accessPoint != null) {
    538             WifiConfiguration config = accessPoint.getConfig();
    539             if (isEditabilityLockedDown(getActivity(), config) && accessPoint.isActive()) {
    540                 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getActivity(),
    541                         RestrictedLockUtils.getDeviceOwner(getActivity()));
    542                 return;
    543             }
    544         }
    545 
    546         if (mDialog != null) {
    547             removeDialog(WIFI_DIALOG_ID);
    548             mDialog = null;
    549         }
    550 
    551         // Save the access point and edit mode
    552         mDlgAccessPoint = accessPoint;
    553         mDialogMode = dialogMode;
    554 
    555         showDialog(WIFI_DIALOG_ID);
    556     }
    557 
    558     @Override
    559     public Dialog onCreateDialog(int dialogId) {
    560         switch (dialogId) {
    561             case WIFI_DIALOG_ID:
    562                 AccessPoint ap = mDlgAccessPoint; // For manual launch
    563                 if (ap == null) { // For re-launch from saved state
    564                     if (mAccessPointSavedState != null) {
    565                         ap = new AccessPoint(getActivity(), mAccessPointSavedState);
    566                         // For repeated orientation changes
    567                         mDlgAccessPoint = ap;
    568                         // Reset the saved access point data
    569                         mAccessPointSavedState = null;
    570                     }
    571                 }
    572                 // If it's null, fine, it's for Add Network
    573                 mSelectedAccessPoint = ap;
    574                 mDialog = new WifiDialog(getActivity(), this, ap, mDialogMode,
    575                         /* no hide submit/connect */ false);
    576                 return mDialog;
    577             case WPS_PBC_DIALOG_ID:
    578                 return new WpsDialog(getActivity(), WpsInfo.PBC);
    579             case WPS_PIN_DIALOG_ID:
    580                 return new WpsDialog(getActivity(), WpsInfo.DISPLAY);
    581             case WRITE_NFC_DIALOG_ID:
    582                 if (mSelectedAccessPoint != null) {
    583                     mWifiToNfcDialog = new WriteWifiConfigToNfcDialog(
    584                             getActivity(), mSelectedAccessPoint.getConfig().networkId,
    585                             mSelectedAccessPoint.getSecurity(),
    586                             mWifiManager);
    587                 } else if (mWifiNfcDialogSavedState != null) {
    588                     mWifiToNfcDialog = new WriteWifiConfigToNfcDialog(
    589                             getActivity(), mWifiNfcDialogSavedState, mWifiManager);
    590                 }
    591 
    592                 return mWifiToNfcDialog;
    593         }
    594         return super.onCreateDialog(dialogId);
    595     }
    596 
    597     /**
    598      * Shows the latest access points available with supplemental information like
    599      * the strength of network and the security for it.
    600      */
    601     @Override
    602     public void onAccessPointsChanged() {
    603         // Safeguard from some delayed event handling
    604         if (getActivity() == null) return;
    605         if (isUiRestricted()) {
    606             if (!isUiRestrictedByOnlyAdmin()) {
    607                 addMessagePreference(R.string.wifi_empty_list_user_restricted);
    608             }
    609             getPreferenceScreen().removeAll();
    610             return;
    611         }
    612         final int wifiState = mWifiManager.getWifiState();
    613 
    614         switch (wifiState) {
    615             case WifiManager.WIFI_STATE_ENABLED:
    616                 // AccessPoints are automatically sorted with TreeSet.
    617                 final Collection<AccessPoint> accessPoints =
    618                         mWifiTracker.getAccessPoints();
    619 
    620                 boolean hasAvailableAccessPoints = false;
    621                 int index = 0;
    622                 cacheRemoveAllPrefs(getPreferenceScreen());
    623                 for (AccessPoint accessPoint : accessPoints) {
    624                     // Ignore access points that are out of range.
    625                     if (accessPoint.getLevel() != -1) {
    626                         String key = accessPoint.getBssid();
    627                         if (TextUtils.isEmpty(key)) {
    628                             key = accessPoint.getSsidStr();
    629                         }
    630                         hasAvailableAccessPoints = true;
    631                         LongPressAccessPointPreference pref = (LongPressAccessPointPreference)
    632                                 getCachedPreference(key);
    633                         if (pref != null) {
    634                             pref.setOrder(index++);
    635                             continue;
    636                         }
    637                         LongPressAccessPointPreference
    638                                 preference = new LongPressAccessPointPreference(accessPoint,
    639                                 getPrefContext(), mUserBadgeCache, false,
    640                                 R.drawable.ic_wifi_signal_0, this);
    641                         preference.setKey(key);
    642                         preference.setOrder(index++);
    643                         if (mOpenSsid != null && mOpenSsid.equals(accessPoint.getSsidStr())
    644                                 && !accessPoint.isSaved()
    645                                 && accessPoint.getSecurity() != AccessPoint.SECURITY_NONE) {
    646                             onPreferenceTreeClick(preference);
    647                             mOpenSsid = null;
    648                         }
    649                         getPreferenceScreen().addPreference(preference);
    650                         accessPoint.setListener(this);
    651                         preference.refresh();
    652                     }
    653                 }
    654                 removeCachedPrefs(getPreferenceScreen());
    655                 if (!hasAvailableAccessPoints) {
    656                     setProgressBarVisible(true);
    657                     Preference pref = new Preference(getContext()) {
    658                         @Override
    659                         public void onBindViewHolder(PreferenceViewHolder holder) {
    660                             super.onBindViewHolder(holder);
    661                             // Show a line on each side of add network.
    662                             holder.setDividerAllowedBelow(true);
    663                         }
    664                     };
    665                     pref.setSelectable(false);
    666                     pref.setSummary(R.string.wifi_empty_list_wifi_on);
    667                     pref.setOrder(0);
    668                     pref.setKey(PREF_KEY_EMPTY_WIFI_LIST);
    669                     getPreferenceScreen().addPreference(pref);
    670                     mAddPreference.setOrder(1);
    671                     getPreferenceScreen().addPreference(mAddPreference);
    672                 } else {
    673                     mAddPreference.setOrder(index++);
    674                     getPreferenceScreen().addPreference(mAddPreference);
    675                     setProgressBarVisible(false);
    676                 }
    677                 if (mScanMenuItem != null) {
    678                     mScanMenuItem.setEnabled(true);
    679                 }
    680                 break;
    681 
    682             case WifiManager.WIFI_STATE_ENABLING:
    683                 getPreferenceScreen().removeAll();
    684                 setProgressBarVisible(true);
    685                 break;
    686 
    687             case WifiManager.WIFI_STATE_DISABLING:
    688                 addMessagePreference(R.string.wifi_stopping);
    689                 setProgressBarVisible(true);
    690                 break;
    691 
    692             case WifiManager.WIFI_STATE_DISABLED:
    693                 setOffMessage();
    694                 setProgressBarVisible(false);
    695                 if (mScanMenuItem != null) {
    696                     mScanMenuItem.setEnabled(false);
    697                 }
    698                 break;
    699         }
    700     }
    701 
    702     private void setOffMessage() {
    703         if (isUiRestricted()) {
    704             if (!isUiRestrictedByOnlyAdmin()) {
    705                 addMessagePreference(R.string.wifi_empty_list_user_restricted);
    706             }
    707             getPreferenceScreen().removeAll();
    708             return;
    709         }
    710 
    711         TextView emptyTextView = getEmptyTextView();
    712         if (emptyTextView == null) {
    713             return;
    714         }
    715 
    716         final CharSequence briefText = getText(R.string.wifi_empty_list_wifi_off);
    717 
    718         // Don't use WifiManager.isScanAlwaysAvailable() to check the Wi-Fi scanning mode. Instead,
    719         // read the system settings directly. Because when the device is in Airplane mode, even if
    720         // Wi-Fi scanning mode is on, WifiManager.isScanAlwaysAvailable() still returns "off".
    721         final ContentResolver resolver = getActivity().getContentResolver();
    722         final boolean wifiScanningMode = Settings.Global.getInt(
    723                 resolver, Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 1;
    724 
    725         if (!wifiScanningMode) {
    726             // Show only the brief text if the user is not allowed to configure scanning settings,
    727             // or the scanning mode has been turned off.
    728             emptyTextView.setText(briefText, BufferType.SPANNABLE);
    729         } else {
    730             // Append the description of scanning settings with link.
    731             final StringBuilder contentBuilder = new StringBuilder();
    732             contentBuilder.append(briefText);
    733             contentBuilder.append("\n\n");
    734             contentBuilder.append(getText(R.string.wifi_scan_notify_text));
    735             LinkifyUtils.linkify(emptyTextView, contentBuilder, new LinkifyUtils.OnClickListener() {
    736                 @Override
    737                 public void onClick() {
    738                     final SettingsActivity activity =
    739                             (SettingsActivity) WifiSettings.this.getActivity();
    740                     activity.startPreferencePanel(ScanningSettings.class.getName(), null,
    741                             R.string.location_scanning_screen_title, null, null, 0);
    742                 }
    743             });
    744         }
    745         // Embolden and enlarge the brief description anyway.
    746         Spannable boldSpan = (Spannable) emptyTextView.getText();
    747         boldSpan.setSpan(
    748                 new TextAppearanceSpan(getActivity(), android.R.style.TextAppearance_Medium), 0,
    749                 briefText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    750         getPreferenceScreen().removeAll();
    751     }
    752 
    753     private void addMessagePreference(int messageId) {
    754         TextView emptyTextView = getEmptyTextView();
    755         if (emptyTextView != null) emptyTextView.setText(messageId);
    756         getPreferenceScreen().removeAll();
    757     }
    758 
    759     protected void setProgressBarVisible(boolean visible) {
    760         if (mProgressHeader != null) {
    761             mProgressHeader.setVisibility(visible ? View.VISIBLE : View.GONE);
    762         }
    763     }
    764 
    765     @Override
    766     public void onWifiStateChanged(int state) {
    767         switch (state) {
    768             case WifiManager.WIFI_STATE_ENABLING:
    769                 addMessagePreference(R.string.wifi_starting);
    770                 setProgressBarVisible(true);
    771                 break;
    772 
    773             case WifiManager.WIFI_STATE_DISABLED:
    774                 setOffMessage();
    775                 setProgressBarVisible(false);
    776                 break;
    777         }
    778     }
    779 
    780     @Override
    781     public void onConnectedChanged() {
    782         changeNextButtonState(mWifiTracker.isConnected());
    783     }
    784 
    785     /**
    786      * Renames/replaces "Next" button when appropriate. "Next" button usually exists in
    787      * Wifi setup screens, not in usual wifi settings screen.
    788      *
    789      * @param enabled true when the device is connected to a wifi network.
    790      */
    791     private void changeNextButtonState(boolean enabled) {
    792         if (mEnableNextOnConnection && hasNextButton()) {
    793             getNextButton().setEnabled(enabled);
    794         }
    795     }
    796 
    797     @Override
    798     public void onForget(WifiDialog dialog) {
    799         forget();
    800     }
    801 
    802     @Override
    803     public void onSubmit(WifiDialog dialog) {
    804         if (mDialog != null) {
    805             submit(mDialog.getController());
    806         }
    807     }
    808 
    809     /* package */ void submit(WifiConfigController configController) {
    810 
    811         final WifiConfiguration config = configController.getConfig();
    812 
    813         if (config == null) {
    814             if (mSelectedAccessPoint != null
    815                     && mSelectedAccessPoint.isSaved()) {
    816                 connect(mSelectedAccessPoint.getConfig());
    817             }
    818         } else if (configController.getMode() == WifiConfigUiBase.MODE_MODIFY) {
    819             mWifiManager.save(config, mSaveListener);
    820         } else {
    821             mWifiManager.save(config, mSaveListener);
    822             if (mSelectedAccessPoint != null) { // Not an "Add network"
    823                 connect(config);
    824             }
    825         }
    826 
    827         mWifiTracker.resumeScanning();
    828     }
    829 
    830     /* package */ void forget() {
    831         MetricsLogger.action(getActivity(), MetricsEvent.ACTION_WIFI_FORGET);
    832         if (!mSelectedAccessPoint.isSaved()) {
    833             if (mSelectedAccessPoint.getNetworkInfo() != null &&
    834                     mSelectedAccessPoint.getNetworkInfo().getState() != State.DISCONNECTED) {
    835                 // Network is active but has no network ID - must be ephemeral.
    836                 mWifiManager.disableEphemeralNetwork(
    837                         AccessPoint.convertToQuotedString(mSelectedAccessPoint.getSsidStr()));
    838             } else {
    839                 // Should not happen, but a monkey seems to trigger it
    840                 Log.e(TAG, "Failed to forget invalid network " + mSelectedAccessPoint.getConfig());
    841                 return;
    842             }
    843         } else {
    844             mWifiManager.forget(mSelectedAccessPoint.getConfig().networkId, mForgetListener);
    845         }
    846 
    847         mWifiTracker.resumeScanning();
    848 
    849         // We need to rename/replace "Next" button in wifi setup context.
    850         changeNextButtonState(false);
    851     }
    852 
    853     protected void connect(final WifiConfiguration config) {
    854         MetricsLogger.action(getActivity(), MetricsEvent.ACTION_WIFI_CONNECT);
    855         mWifiManager.connect(config, mConnectListener);
    856     }
    857 
    858     protected void connect(final int networkId) {
    859         MetricsLogger.action(getActivity(), MetricsEvent.ACTION_WIFI_CONNECT);
    860         mWifiManager.connect(networkId, mConnectListener);
    861     }
    862 
    863     /**
    864      * Called when "add network" button is pressed.
    865      */
    866     /* package */ void onAddNetworkPressed() {
    867         MetricsLogger.action(getActivity(), MetricsEvent.ACTION_WIFI_ADD_NETWORK);
    868         // No exact access point is selected.
    869         mSelectedAccessPoint = null;
    870         showDialog(null, WifiConfigUiBase.MODE_CONNECT);
    871     }
    872 
    873     @Override
    874     protected int getHelpResource() {
    875         return R.string.help_url_wifi;
    876     }
    877 
    878     @Override
    879     public void onAccessPointChanged(final AccessPoint accessPoint) {
    880         View view = getView();
    881         if (view != null) {
    882             view.post(new Runnable() {
    883                 @Override
    884                 public void run() {
    885                     Object tag = accessPoint.getTag();
    886                     if (tag != null) {
    887                         ((LongPressAccessPointPreference) tag).refresh();
    888                     }
    889                 }
    890             });
    891         }
    892     }
    893 
    894     @Override
    895     public void onLevelChanged(AccessPoint accessPoint) {
    896         ((LongPressAccessPointPreference) accessPoint.getTag()).onLevelChanged();
    897     }
    898 
    899     public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
    900         new BaseSearchIndexProvider() {
    901             @Override
    902             public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
    903                 final List<SearchIndexableRaw> result = new ArrayList<>();
    904                 final Resources res = context.getResources();
    905 
    906                 // Add fragment title
    907                 SearchIndexableRaw data = new SearchIndexableRaw(context);
    908                 data.title = res.getString(R.string.wifi_settings);
    909                 data.screenTitle = res.getString(R.string.wifi_settings);
    910                 data.keywords = res.getString(R.string.keywords_wifi);
    911                 result.add(data);
    912 
    913                 // Add saved Wi-Fi access points
    914                 final Collection<AccessPoint> accessPoints =
    915                         WifiTracker.getCurrentAccessPoints(context, true, false, false);
    916                 for (AccessPoint accessPoint : accessPoints) {
    917                     data = new SearchIndexableRaw(context);
    918                     data.title = accessPoint.getSsidStr();
    919                     data.screenTitle = res.getString(R.string.wifi_settings);
    920                     data.enabled = enabled;
    921                     result.add(data);
    922                 }
    923 
    924                 return result;
    925             }
    926         };
    927 
    928     /**
    929      * Returns true if the config is not editable through Settings.
    930      * @param context Context of caller
    931      * @param config The WiFi config.
    932      * @return true if the config is not editable through Settings.
    933      */
    934     static boolean isEditabilityLockedDown(Context context, WifiConfiguration config) {
    935         return !canModifyNetwork(context, config);
    936     }
    937 
    938     /**
    939      * This method is a stripped version of WifiConfigStore.canModifyNetwork.
    940      * TODO: refactor to have only one method.
    941      * @param context Context of caller
    942      * @param config The WiFi config.
    943      * @return true if Settings can modify the config.
    944      */
    945     static boolean canModifyNetwork(Context context, WifiConfiguration config) {
    946         if (config == null) {
    947             return true;
    948         }
    949 
    950         final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
    951                 Context.DEVICE_POLICY_SERVICE);
    952 
    953         // Check if device has DPM capability. If it has and dpm is still null, then we
    954         // treat this case with suspicion and bail out.
    955         final PackageManager pm = context.getPackageManager();
    956         if (pm.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN) && dpm == null) {
    957             return false;
    958         }
    959 
    960         boolean isConfigEligibleForLockdown = false;
    961         if (dpm != null) {
    962             final ComponentName deviceOwner = dpm.getDeviceOwnerComponentOnAnyUser();
    963             if (deviceOwner != null) {
    964                 final int deviceOwnerUserId = dpm.getDeviceOwnerUserId();
    965                 try {
    966                     final int deviceOwnerUid = pm.getPackageUidAsUser(deviceOwner.getPackageName(),
    967                             deviceOwnerUserId);
    968                     isConfigEligibleForLockdown = deviceOwnerUid == config.creatorUid;
    969                 } catch (NameNotFoundException e) {
    970                     // don't care
    971                 }
    972             }
    973         }
    974         if (!isConfigEligibleForLockdown) {
    975             return true;
    976         }
    977 
    978         final ContentResolver resolver = context.getContentResolver();
    979         final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver,
    980                 Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0;
    981         return !isLockdownFeatureEnabled;
    982     }
    983 
    984     private static class SummaryProvider extends BroadcastReceiver
    985             implements SummaryLoader.SummaryProvider {
    986 
    987         private final Context mContext;
    988         private final WifiManager mWifiManager;
    989         private final WifiStatusTracker mWifiTracker;
    990         private final SummaryLoader mSummaryLoader;
    991 
    992         public SummaryProvider(Context context, SummaryLoader summaryLoader) {
    993             mContext = context;
    994             mSummaryLoader = summaryLoader;
    995             mWifiManager = context.getSystemService(WifiManager.class);
    996             mWifiTracker = new WifiStatusTracker(mWifiManager);
    997         }
    998 
    999         private CharSequence getSummary() {
   1000             if (!mWifiTracker.enabled) {
   1001                 return mContext.getString(R.string.wifi_disabled_generic);
   1002             }
   1003             if (!mWifiTracker.connected) {
   1004                 return mContext.getString(R.string.disconnected);
   1005             }
   1006             return mWifiTracker.ssid;
   1007         }
   1008 
   1009         @Override
   1010         public void setListening(boolean listening) {
   1011             if (listening) {
   1012                 IntentFilter filter = new IntentFilter();
   1013                 filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
   1014                 filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
   1015                 filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
   1016                 mSummaryLoader.registerReceiver(this, filter);
   1017             }
   1018         }
   1019 
   1020         @Override
   1021         public void onReceive(Context context, Intent intent) {
   1022             mWifiTracker.handleBroadcast(intent);
   1023             mSummaryLoader.setSummary(this, getSummary());
   1024         }
   1025     }
   1026 
   1027     public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
   1028             = new SummaryLoader.SummaryProviderFactory() {
   1029         @Override
   1030         public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
   1031                                                                    SummaryLoader summaryLoader) {
   1032             return new SummaryProvider(activity, summaryLoader);
   1033         }
   1034     };
   1035 }
   1036