OSDN Git Service

Merge "Back-port ag/2491664" into lmp-dev am: 41492a1e2c am: cbbdc18c7b am: c84dc8feb...
[android-x86/packages-apps-Settings.git] / src / com / android / settings / wifi / WifiSettings.java
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                 boolean isSavedNetwork = mSelectedAccessPoint.isSaved();
485                 if (isSavedNetwork) {
486                     connect(mSelectedAccessPoint.getConfig(), isSavedNetwork);
487                 } else if (mSelectedAccessPoint.getSecurity() == AccessPoint.SECURITY_NONE) {
488                     /** Bypass dialog for unsecured networks */
489                     mSelectedAccessPoint.generateOpenNetworkConfig();
490                     connect(mSelectedAccessPoint.getConfig(), isSavedNetwork);
491                 } else {
492                     showDialog(mSelectedAccessPoint, WifiConfigUiBase.MODE_CONNECT);
493                 }
494                 return true;
495             }
496             case MENU_ID_FORGET: {
497                 forget();
498                 return true;
499             }
500             case MENU_ID_MODIFY: {
501                 showDialog(mSelectedAccessPoint, WifiConfigUiBase.MODE_MODIFY);
502                 return true;
503             }
504             case MENU_ID_WRITE_NFC:
505                 showDialog(WRITE_NFC_DIALOG_ID);
506                 return true;
507
508         }
509         return super.onContextItemSelected(item);
510     }
511
512     @Override
513     public boolean onPreferenceTreeClick(Preference preference) {
514         if (preference instanceof LongPressAccessPointPreference) {
515             mSelectedAccessPoint = ((LongPressAccessPointPreference) preference).getAccessPoint();
516             if (mSelectedAccessPoint == null) {
517                 return false;
518             }
519             /** Bypass dialog for unsecured, unsaved, and inactive networks */
520             if (mSelectedAccessPoint.getSecurity() == AccessPoint.SECURITY_NONE &&
521                     !mSelectedAccessPoint.isSaved() && !mSelectedAccessPoint.isActive()) {
522                 mSelectedAccessPoint.generateOpenNetworkConfig();
523                 connect(mSelectedAccessPoint.getConfig(), false /* isSavedNetwork */);
524             } else if (mSelectedAccessPoint.isSaved()) {
525                 showDialog(mSelectedAccessPoint, WifiConfigUiBase.MODE_VIEW);
526             } else {
527                 showDialog(mSelectedAccessPoint, WifiConfigUiBase.MODE_CONNECT);
528             }
529         } else if (preference == mAddPreference) {
530             onAddNetworkPressed();
531         } else {
532             return super.onPreferenceTreeClick(preference);
533         }
534         return true;
535     }
536
537     private void showDialog(AccessPoint accessPoint, int dialogMode) {
538         if (accessPoint != null) {
539             WifiConfiguration config = accessPoint.getConfig();
540             if (isEditabilityLockedDown(getActivity(), config) && accessPoint.isActive()) {
541                 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getActivity(),
542                         RestrictedLockUtils.getDeviceOwner(getActivity()));
543                 return;
544             }
545         }
546
547         if (mDialog != null) {
548             removeDialog(WIFI_DIALOG_ID);
549             mDialog = null;
550         }
551
552         // Save the access point and edit mode
553         mDlgAccessPoint = accessPoint;
554         mDialogMode = dialogMode;
555
556         showDialog(WIFI_DIALOG_ID);
557     }
558
559     @Override
560     public Dialog onCreateDialog(int dialogId) {
561         switch (dialogId) {
562             case WIFI_DIALOG_ID:
563                 AccessPoint ap = mDlgAccessPoint; // For manual launch
564                 if (ap == null) { // For re-launch from saved state
565                     if (mAccessPointSavedState != null) {
566                         ap = new AccessPoint(getActivity(), mAccessPointSavedState);
567                         // For repeated orientation changes
568                         mDlgAccessPoint = ap;
569                         // Reset the saved access point data
570                         mAccessPointSavedState = null;
571                     }
572                 }
573                 // If it's null, fine, it's for Add Network
574                 mSelectedAccessPoint = ap;
575                 mDialog = new WifiDialog(getActivity(), this, ap, mDialogMode,
576                         /* no hide submit/connect */ false);
577                 return mDialog;
578             case WPS_PBC_DIALOG_ID:
579                 return new WpsDialog(getActivity(), WpsInfo.PBC);
580             case WPS_PIN_DIALOG_ID:
581                 return new WpsDialog(getActivity(), WpsInfo.DISPLAY);
582             case WRITE_NFC_DIALOG_ID:
583                 if (mSelectedAccessPoint != null) {
584                     mWifiToNfcDialog = new WriteWifiConfigToNfcDialog(
585                             getActivity(), mSelectedAccessPoint.getConfig().networkId,
586                             mSelectedAccessPoint.getSecurity(),
587                             mWifiManager);
588                 } else if (mWifiNfcDialogSavedState != null) {
589                     mWifiToNfcDialog = new WriteWifiConfigToNfcDialog(
590                             getActivity(), mWifiNfcDialogSavedState, mWifiManager);
591                 }
592
593                 return mWifiToNfcDialog;
594         }
595         return super.onCreateDialog(dialogId);
596     }
597
598     /**
599      * Shows the latest access points available with supplemental information like
600      * the strength of network and the security for it.
601      */
602     @Override
603     public void onAccessPointsChanged() {
604         // Safeguard from some delayed event handling
605         if (getActivity() == null) return;
606         if (isUiRestricted()) {
607             if (!isUiRestrictedByOnlyAdmin()) {
608                 addMessagePreference(R.string.wifi_empty_list_user_restricted);
609             }
610             getPreferenceScreen().removeAll();
611             return;
612         }
613         final int wifiState = mWifiManager.getWifiState();
614
615         switch (wifiState) {
616             case WifiManager.WIFI_STATE_ENABLED:
617                 // AccessPoints are automatically sorted with TreeSet.
618                 final Collection<AccessPoint> accessPoints =
619                         mWifiTracker.getAccessPoints();
620
621                 boolean hasAvailableAccessPoints = false;
622                 int index = 0;
623                 cacheRemoveAllPrefs(getPreferenceScreen());
624                 for (AccessPoint accessPoint : accessPoints) {
625                     // Ignore access points that are out of range.
626                     if (accessPoint.getLevel() != -1) {
627                         String key = accessPoint.getBssid();
628                         if (TextUtils.isEmpty(key)) {
629                             key = accessPoint.getSsidStr();
630                         }
631                         hasAvailableAccessPoints = true;
632                         LongPressAccessPointPreference pref = (LongPressAccessPointPreference)
633                                 getCachedPreference(key);
634                         if (pref != null) {
635                             pref.setOrder(index++);
636                             continue;
637                         }
638                         LongPressAccessPointPreference
639                                 preference = new LongPressAccessPointPreference(accessPoint,
640                                 getPrefContext(), mUserBadgeCache, false,
641                                 R.drawable.ic_wifi_signal_0, this);
642                         preference.setKey(key);
643                         preference.setOrder(index++);
644                         if (mOpenSsid != null && mOpenSsid.equals(accessPoint.getSsidStr())
645                                 && !accessPoint.isSaved()
646                                 && accessPoint.getSecurity() != AccessPoint.SECURITY_NONE) {
647                             onPreferenceTreeClick(preference);
648                             mOpenSsid = null;
649                         }
650                         getPreferenceScreen().addPreference(preference);
651                         accessPoint.setListener(this);
652                         preference.refresh();
653                     }
654                 }
655                 removeCachedPrefs(getPreferenceScreen());
656                 if (!hasAvailableAccessPoints) {
657                     setProgressBarVisible(true);
658                     Preference pref = new Preference(getContext()) {
659                         @Override
660                         public void onBindViewHolder(PreferenceViewHolder holder) {
661                             super.onBindViewHolder(holder);
662                             // Show a line on each side of add network.
663                             holder.setDividerAllowedBelow(true);
664                         }
665                     };
666                     pref.setSelectable(false);
667                     pref.setSummary(R.string.wifi_empty_list_wifi_on);
668                     pref.setOrder(0);
669                     pref.setKey(PREF_KEY_EMPTY_WIFI_LIST);
670                     getPreferenceScreen().addPreference(pref);
671                     mAddPreference.setOrder(1);
672                     getPreferenceScreen().addPreference(mAddPreference);
673                 } else {
674                     mAddPreference.setOrder(index++);
675                     getPreferenceScreen().addPreference(mAddPreference);
676                     setProgressBarVisible(false);
677                 }
678                 if (mScanMenuItem != null) {
679                     mScanMenuItem.setEnabled(true);
680                 }
681                 break;
682
683             case WifiManager.WIFI_STATE_ENABLING:
684                 getPreferenceScreen().removeAll();
685                 setProgressBarVisible(true);
686                 break;
687
688             case WifiManager.WIFI_STATE_DISABLING:
689                 addMessagePreference(R.string.wifi_stopping);
690                 setProgressBarVisible(true);
691                 break;
692
693             case WifiManager.WIFI_STATE_DISABLED:
694                 setOffMessage();
695                 setProgressBarVisible(false);
696                 if (mScanMenuItem != null) {
697                     mScanMenuItem.setEnabled(false);
698                 }
699                 break;
700         }
701     }
702
703     private void setOffMessage() {
704         if (isUiRestricted()) {
705             if (!isUiRestrictedByOnlyAdmin()) {
706                 addMessagePreference(R.string.wifi_empty_list_user_restricted);
707             }
708             getPreferenceScreen().removeAll();
709             return;
710         }
711
712         TextView emptyTextView = getEmptyTextView();
713         if (emptyTextView == null) {
714             return;
715         }
716
717         final CharSequence briefText = getText(R.string.wifi_empty_list_wifi_off);
718
719         // Don't use WifiManager.isScanAlwaysAvailable() to check the Wi-Fi scanning mode. Instead,
720         // read the system settings directly. Because when the device is in Airplane mode, even if
721         // Wi-Fi scanning mode is on, WifiManager.isScanAlwaysAvailable() still returns "off".
722         final ContentResolver resolver = getActivity().getContentResolver();
723         final boolean wifiScanningMode = Settings.Global.getInt(
724                 resolver, Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 1;
725
726         if (!wifiScanningMode) {
727             // Show only the brief text if the user is not allowed to configure scanning settings,
728             // or the scanning mode has been turned off.
729             emptyTextView.setText(briefText, BufferType.SPANNABLE);
730         } else {
731             // Append the description of scanning settings with link.
732             final StringBuilder contentBuilder = new StringBuilder();
733             contentBuilder.append(briefText);
734             contentBuilder.append("\n\n");
735             contentBuilder.append(getText(R.string.wifi_scan_notify_text));
736             LinkifyUtils.linkify(emptyTextView, contentBuilder, new LinkifyUtils.OnClickListener() {
737                 @Override
738                 public void onClick() {
739                     final SettingsActivity activity =
740                             (SettingsActivity) WifiSettings.this.getActivity();
741                     activity.startPreferencePanel(ScanningSettings.class.getName(), null,
742                             R.string.location_scanning_screen_title, null, null, 0);
743                 }
744             });
745         }
746         // Embolden and enlarge the brief description anyway.
747         Spannable boldSpan = (Spannable) emptyTextView.getText();
748         boldSpan.setSpan(
749                 new TextAppearanceSpan(getActivity(), android.R.style.TextAppearance_Medium), 0,
750                 briefText.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
751         getPreferenceScreen().removeAll();
752     }
753
754     private void addMessagePreference(int messageId) {
755         TextView emptyTextView = getEmptyTextView();
756         if (emptyTextView != null) emptyTextView.setText(messageId);
757         getPreferenceScreen().removeAll();
758     }
759
760     protected void setProgressBarVisible(boolean visible) {
761         if (mProgressHeader != null) {
762             mProgressHeader.setVisibility(visible ? View.VISIBLE : View.GONE);
763         }
764     }
765
766     @Override
767     public void onWifiStateChanged(int state) {
768         switch (state) {
769             case WifiManager.WIFI_STATE_ENABLING:
770                 addMessagePreference(R.string.wifi_starting);
771                 setProgressBarVisible(true);
772                 break;
773
774             case WifiManager.WIFI_STATE_DISABLED:
775                 setOffMessage();
776                 setProgressBarVisible(false);
777                 break;
778         }
779     }
780
781     @Override
782     public void onConnectedChanged() {
783         changeNextButtonState(mWifiTracker.isConnected());
784     }
785
786     /**
787      * Renames/replaces "Next" button when appropriate. "Next" button usually exists in
788      * Wifi setup screens, not in usual wifi settings screen.
789      *
790      * @param enabled true when the device is connected to a wifi network.
791      */
792     private void changeNextButtonState(boolean enabled) {
793         if (mEnableNextOnConnection && hasNextButton()) {
794             getNextButton().setEnabled(enabled);
795         }
796     }
797
798     @Override
799     public void onForget(WifiDialog dialog) {
800         forget();
801     }
802
803     @Override
804     public void onSubmit(WifiDialog dialog) {
805         if (mDialog != null) {
806             submit(mDialog.getController());
807         }
808     }
809
810     /* package */ void submit(WifiConfigController configController) {
811
812         final WifiConfiguration config = configController.getConfig();
813
814         if (config == null) {
815             if (mSelectedAccessPoint != null
816                     && mSelectedAccessPoint.isSaved()) {
817                 connect(mSelectedAccessPoint.getConfig(), true /* isSavedNetwork */);
818             }
819         } else if (configController.getMode() == WifiConfigUiBase.MODE_MODIFY) {
820             mWifiManager.save(config, mSaveListener);
821         } else {
822             mWifiManager.save(config, mSaveListener);
823             if (mSelectedAccessPoint != null) { // Not an "Add network"
824                 connect(config, false /* isSavedNetwork */);
825             }
826         }
827
828         mWifiTracker.resumeScanning();
829     }
830
831     /* package */ void forget() {
832         MetricsLogger.action(getActivity(), MetricsEvent.ACTION_WIFI_FORGET);
833         if (!mSelectedAccessPoint.isSaved()) {
834             if (mSelectedAccessPoint.getNetworkInfo() != null &&
835                     mSelectedAccessPoint.getNetworkInfo().getState() != State.DISCONNECTED) {
836                 // Network is active but has no network ID - must be ephemeral.
837                 mWifiManager.disableEphemeralNetwork(
838                         AccessPoint.convertToQuotedString(mSelectedAccessPoint.getSsidStr()));
839             } else {
840                 // Should not happen, but a monkey seems to trigger it
841                 Log.e(TAG, "Failed to forget invalid network " + mSelectedAccessPoint.getConfig());
842                 return;
843             }
844         } else {
845             mWifiManager.forget(mSelectedAccessPoint.getConfig().networkId, mForgetListener);
846         }
847
848         mWifiTracker.resumeScanning();
849
850         // We need to rename/replace "Next" button in wifi setup context.
851         changeNextButtonState(false);
852     }
853
854     protected void connect(final WifiConfiguration config, boolean isSavedNetwork) {
855         // Log subtype if configuration is a saved network.
856         MetricsLogger.action(getActivity(), MetricsEvent.ACTION_WIFI_CONNECT,
857                 isSavedNetwork);
858         mWifiManager.connect(config, mConnectListener);
859     }
860
861     protected void connect(final int networkId, boolean isSavedNetwork) {
862         // Log subtype if configuration is a saved network.
863         MetricsLogger.action(getActivity(), MetricsEvent.ACTION_WIFI_CONNECT,
864                 isSavedNetwork);
865         mWifiManager.connect(networkId, mConnectListener);
866     }
867
868     /**
869      * Called when "add network" button is pressed.
870      */
871     /* package */ void onAddNetworkPressed() {
872         MetricsLogger.action(getActivity(), MetricsEvent.ACTION_WIFI_ADD_NETWORK);
873         // No exact access point is selected.
874         mSelectedAccessPoint = null;
875         showDialog(null, WifiConfigUiBase.MODE_CONNECT);
876     }
877
878     @Override
879     protected int getHelpResource() {
880         return R.string.help_url_wifi;
881     }
882
883     @Override
884     public void onAccessPointChanged(final AccessPoint accessPoint) {
885         View view = getView();
886         if (view != null) {
887             view.post(new Runnable() {
888                 @Override
889                 public void run() {
890                     Object tag = accessPoint.getTag();
891                     if (tag != null) {
892                         ((LongPressAccessPointPreference) tag).refresh();
893                     }
894                 }
895             });
896         }
897     }
898
899     @Override
900     public void onLevelChanged(AccessPoint accessPoint) {
901         ((LongPressAccessPointPreference) accessPoint.getTag()).onLevelChanged();
902     }
903
904     public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
905         new BaseSearchIndexProvider() {
906             @Override
907             public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
908                 final List<SearchIndexableRaw> result = new ArrayList<>();
909                 final Resources res = context.getResources();
910
911                 // Add fragment title
912                 SearchIndexableRaw data = new SearchIndexableRaw(context);
913                 data.title = res.getString(R.string.wifi_settings);
914                 data.screenTitle = res.getString(R.string.wifi_settings);
915                 data.keywords = res.getString(R.string.keywords_wifi);
916                 result.add(data);
917
918                 // Add saved Wi-Fi access points
919                 final Collection<AccessPoint> accessPoints =
920                         WifiTracker.getCurrentAccessPoints(context, true, false, false);
921                 for (AccessPoint accessPoint : accessPoints) {
922                     data = new SearchIndexableRaw(context);
923                     data.title = accessPoint.getSsidStr();
924                     data.screenTitle = res.getString(R.string.wifi_settings);
925                     data.enabled = enabled;
926                     result.add(data);
927                 }
928
929                 return result;
930             }
931         };
932
933     /**
934      * Returns true if the config is not editable through Settings.
935      * @param context Context of caller
936      * @param config The WiFi config.
937      * @return true if the config is not editable through Settings.
938      */
939     static boolean isEditabilityLockedDown(Context context, WifiConfiguration config) {
940         return !canModifyNetwork(context, config);
941     }
942
943     /**
944      * This method is a stripped version of WifiConfigStore.canModifyNetwork.
945      * TODO: refactor to have only one method.
946      * @param context Context of caller
947      * @param config The WiFi config.
948      * @return true if Settings can modify the config.
949      */
950     static boolean canModifyNetwork(Context context, WifiConfiguration config) {
951         if (config == null) {
952             return true;
953         }
954
955         final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
956                 Context.DEVICE_POLICY_SERVICE);
957
958         // Check if device has DPM capability. If it has and dpm is still null, then we
959         // treat this case with suspicion and bail out.
960         final PackageManager pm = context.getPackageManager();
961         if (pm.hasSystemFeature(PackageManager.FEATURE_DEVICE_ADMIN) && dpm == null) {
962             return false;
963         }
964
965         boolean isConfigEligibleForLockdown = false;
966         if (dpm != null) {
967             final ComponentName deviceOwner = dpm.getDeviceOwnerComponentOnAnyUser();
968             if (deviceOwner != null) {
969                 final int deviceOwnerUserId = dpm.getDeviceOwnerUserId();
970                 try {
971                     final int deviceOwnerUid = pm.getPackageUidAsUser(deviceOwner.getPackageName(),
972                             deviceOwnerUserId);
973                     isConfigEligibleForLockdown = deviceOwnerUid == config.creatorUid;
974                 } catch (NameNotFoundException e) {
975                     // don't care
976                 }
977             }
978         }
979         if (!isConfigEligibleForLockdown) {
980             return true;
981         }
982
983         final ContentResolver resolver = context.getContentResolver();
984         final boolean isLockdownFeatureEnabled = Settings.Global.getInt(resolver,
985                 Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, 0) != 0;
986         return !isLockdownFeatureEnabled;
987     }
988
989     private static class SummaryProvider extends BroadcastReceiver
990             implements SummaryLoader.SummaryProvider {
991
992         private final Context mContext;
993         private final WifiManager mWifiManager;
994         private final WifiStatusTracker mWifiTracker;
995         private final SummaryLoader mSummaryLoader;
996
997         public SummaryProvider(Context context, SummaryLoader summaryLoader) {
998             mContext = context;
999             mSummaryLoader = summaryLoader;
1000             mWifiManager = context.getSystemService(WifiManager.class);
1001             mWifiTracker = new WifiStatusTracker(mWifiManager);
1002         }
1003
1004         private CharSequence getSummary() {
1005             if (!mWifiTracker.enabled) {
1006                 return mContext.getString(R.string.wifi_disabled_generic);
1007             }
1008             if (!mWifiTracker.connected) {
1009                 return mContext.getString(R.string.disconnected);
1010             }
1011             return mWifiTracker.ssid;
1012         }
1013
1014         @Override
1015         public void setListening(boolean listening) {
1016             if (listening) {
1017                 IntentFilter filter = new IntentFilter();
1018                 filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
1019                 filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
1020                 filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
1021                 mSummaryLoader.registerReceiver(this, filter);
1022             }
1023         }
1024
1025         @Override
1026         public void onReceive(Context context, Intent intent) {
1027             mWifiTracker.handleBroadcast(intent);
1028             mSummaryLoader.setSummary(this, getSummary());
1029         }
1030     }
1031
1032     public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
1033             = new SummaryLoader.SummaryProviderFactory() {
1034         @Override
1035         public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
1036                                                                    SummaryLoader summaryLoader) {
1037             return new SummaryProvider(activity, summaryLoader);
1038         }
1039     };
1040 }