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