• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.wifi;
18 
19 import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
20 import static android.os.UserManager.DISALLOW_CONFIG_WIFI;
21 
22 import android.annotation.NonNull;
23 import android.app.Activity;
24 import android.app.Dialog;
25 import android.app.settings.SettingsEnums;
26 import android.content.ContentResolver;
27 import android.content.Context;
28 import android.content.DialogInterface;
29 import android.content.Intent;
30 import android.content.res.Resources;
31 import android.net.ConnectivityManager;
32 import android.net.Network;
33 import android.net.NetworkInfo;
34 import android.net.NetworkInfo.State;
35 import android.net.NetworkRequest;
36 import android.net.NetworkTemplate;
37 import android.net.wifi.WifiConfiguration;
38 import android.net.wifi.WifiManager;
39 import android.os.Bundle;
40 import android.os.Handler;
41 import android.os.Looper;
42 import android.os.PowerManager;
43 import android.provider.Settings;
44 import android.util.FeatureFlagUtils;
45 import android.util.Log;
46 import android.view.ContextMenu;
47 import android.view.ContextMenu.ContextMenuInfo;
48 import android.view.Menu;
49 import android.view.MenuItem;
50 import android.view.View;
51 import android.widget.Toast;
52 
53 import androidx.annotation.VisibleForTesting;
54 import androidx.preference.Preference;
55 import androidx.preference.PreferenceCategory;
56 
57 import com.android.settings.LinkifyUtils;
58 import com.android.settings.R;
59 import com.android.settings.RestrictedSettingsFragment;
60 import com.android.settings.SettingsActivity;
61 import com.android.settings.core.FeatureFlags;
62 import com.android.settings.core.SubSettingLauncher;
63 import com.android.settings.dashboard.SummaryLoader;
64 import com.android.settings.datausage.DataUsageUtils;
65 import com.android.settings.datausage.DataUsagePreference;
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.settings.widget.SummaryUpdater.OnSummaryChangeListener;
71 import com.android.settings.widget.SwitchBarController;
72 import com.android.settings.wifi.details.WifiNetworkDetailsFragment;
73 import com.android.settings.wifi.dpp.WifiDppUtils;
74 import com.android.settingslib.RestrictedLockUtils;
75 import com.android.settingslib.RestrictedLockUtilsInternal;
76 import com.android.settingslib.search.SearchIndexable;
77 import com.android.settingslib.wifi.AccessPoint;
78 import com.android.settingslib.wifi.AccessPoint.AccessPointListener;
79 import com.android.settingslib.wifi.AccessPointPreference;
80 import com.android.settingslib.wifi.WifiSavedConfigUtils;
81 import com.android.settingslib.wifi.WifiTracker;
82 import com.android.settingslib.wifi.WifiTrackerFactory;
83 
84 import java.util.ArrayList;
85 import java.util.List;
86 
87 /**
88  * Two types of UI are provided here.
89  *
90  * The first is for "usual Settings", appearing as any other Setup fragment.
91  *
92  * The second is for Setup Wizard, with a simplified interface that hides the action bar
93  * and menus.
94  */
95 @SearchIndexable
96 public class WifiSettings extends RestrictedSettingsFragment
97         implements Indexable, WifiTracker.WifiListener, AccessPointListener,
98         WifiDialog.WifiDialogListener, DialogInterface.OnDismissListener {
99 
100     private static final String TAG = "WifiSettings";
101 
102     private static final int MENU_ID_CONNECT = Menu.FIRST + 6;
103     @VisibleForTesting
104     static final int MENU_ID_FORGET = Menu.FIRST + 7;
105     private static final int MENU_ID_MODIFY = Menu.FIRST + 8;
106 
107     public static final int WIFI_DIALOG_ID = 1;
108 
109     @VisibleForTesting
110     static final int ADD_NETWORK_REQUEST = 2;
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 
116     private static final String PREF_KEY_EMPTY_WIFI_LIST = "wifi_empty_list";
117     private static final String PREF_KEY_CONNECTED_ACCESS_POINTS = "connected_access_point";
118     private static final String PREF_KEY_ACCESS_POINTS = "access_points";
119     private static final String PREF_KEY_CONFIGURE_WIFI_SETTINGS = "configure_settings";
120     private static final String PREF_KEY_SAVED_NETWORKS = "saved_networks";
121     private static final String PREF_KEY_STATUS_MESSAGE = "wifi_status_message";
122     @VisibleForTesting
123     static final String PREF_KEY_DATA_USAGE = "wifi_data_usage";
124 
125     private static final int REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER = 0;
126 
isVerboseLoggingEnabled()127     private static boolean isVerboseLoggingEnabled() {
128         return WifiTracker.sVerboseLogging || Log.isLoggable(TAG, Log.VERBOSE);
129     }
130 
131     private final Runnable mUpdateAccessPointsRunnable = () -> {
132         updateAccessPointPreferences();
133     };
134     private final Runnable mHideProgressBarRunnable = () -> {
135         setProgressBarVisible(false);
136     };
137 
138     protected WifiManager mWifiManager;
139     private ConnectivityManager mConnectivityManager;
140     private WifiManager.ActionListener mConnectListener;
141     private WifiManager.ActionListener mSaveListener;
142     private WifiManager.ActionListener mForgetListener;
143     private CaptivePortalNetworkCallback mCaptivePortalNetworkCallback;
144 
145     /**
146      * The state of {@link #isUiRestricted()} at {@link #onCreate(Bundle)}}. This is neccesary to
147      * ensure that behavior is consistent if {@link #isUiRestricted()} changes. It could be changed
148      * by the Test DPC tool in AFW mode.
149      */
150     private boolean mIsRestricted;
151 
152     private WifiEnabler mWifiEnabler;
153     // An access point being edited is stored here.
154     private AccessPoint mSelectedAccessPoint;
155 
156     private WifiDialog mDialog;
157 
158     private View mProgressHeader;
159 
160     // this boolean extra specifies whether to disable the Next button when not connected. Used by
161     // account creation outside of setup wizard.
162     private static final String EXTRA_ENABLE_NEXT_ON_CONNECT = "wifi_enable_next_on_connect";
163     // This string extra specifies a network to open the connect dialog on, so the user can enter
164     // network credentials.  This is used by quick settings for secured networks, among other
165     // things.
166     public static final String EXTRA_START_CONNECT_SSID = "wifi_start_connect_ssid";
167 
168     // should Next button only be enabled when we have a connection?
169     private boolean mEnableNextOnConnection;
170 
171     // Save the dialog details
172     private int mDialogMode;
173     private AccessPoint mDlgAccessPoint;
174     private Bundle mAccessPointSavedState;
175 
176     @VisibleForTesting
177     WifiTracker mWifiTracker;
178     private String mOpenSsid;
179 
180     private AccessPointPreference.UserBadgeCache mUserBadgeCache;
181 
182     private PreferenceCategory mConnectedAccessPointPreferenceCategory;
183     private PreferenceCategory mAccessPointsPreferenceCategory;
184     @VisibleForTesting
185     AddWifiNetworkPreference mAddWifiNetworkPreference;
186     @VisibleForTesting
187     Preference mConfigureWifiSettingsPreference;
188     @VisibleForTesting
189     Preference mSavedNetworksPreference;
190     @VisibleForTesting
191     DataUsagePreference mDataUsagePreference;
192     private LinkablePreference mStatusMessagePreference;
193 
194     // For Search
195     public static final String DATA_KEY_REFERENCE = "main_toggle_wifi";
196 
197     /**
198      * Tracks whether the user initiated a connection via clicking in order to autoscroll to the
199      * network once connected.
200      */
201     private boolean mClickedConnect;
202 
203     /* End of "used in Wifi Setup context" */
204 
WifiSettings()205     public WifiSettings() {
206         super(DISALLOW_CONFIG_WIFI);
207     }
208 
209     @Override
onViewCreated(View view, Bundle savedInstanceState)210     public void onViewCreated(View view, Bundle savedInstanceState) {
211         super.onViewCreated(view, savedInstanceState);
212         final Activity activity = getActivity();
213         if (activity != null) {
214             mProgressHeader = setPinnedHeaderView(R.layout.progress_header)
215                     .findViewById(R.id.progress_bar_animation);
216             setProgressBarVisible(false);
217         }
218         ((SettingsActivity) activity).getSwitchBar().setSwitchBarText(
219                 R.string.wifi_settings_master_switch_title,
220                 R.string.wifi_settings_master_switch_title);
221     }
222 
223     @Override
onCreate(Bundle icicle)224     public void onCreate(Bundle icicle) {
225         super.onCreate(icicle);
226 
227         // TODO(b/37429702): Add animations and preference comparator back after initial screen is
228         // loaded (ODR).
229         setAnimationAllowed(false);
230 
231         addPreferences();
232 
233         mIsRestricted = isUiRestricted();
234     }
235 
addPreferences()236     private void addPreferences() {
237         addPreferencesFromResource(R.xml.wifi_settings);
238 
239         mConnectedAccessPointPreferenceCategory =
240                 (PreferenceCategory) findPreference(PREF_KEY_CONNECTED_ACCESS_POINTS);
241         mAccessPointsPreferenceCategory =
242                 (PreferenceCategory) findPreference(PREF_KEY_ACCESS_POINTS);
243         mConfigureWifiSettingsPreference = findPreference(PREF_KEY_CONFIGURE_WIFI_SETTINGS);
244         mSavedNetworksPreference = findPreference(PREF_KEY_SAVED_NETWORKS);
245         mAddWifiNetworkPreference = new AddWifiNetworkPreference(getPrefContext());
246         mStatusMessagePreference = (LinkablePreference) findPreference(PREF_KEY_STATUS_MESSAGE);
247         mUserBadgeCache = new AccessPointPreference.UserBadgeCache(getPackageManager());
248         mDataUsagePreference = findPreference(PREF_KEY_DATA_USAGE);
249         mDataUsagePreference.setVisible(DataUsageUtils.hasWifiRadio(getContext()));
250         mDataUsagePreference.setTemplate(NetworkTemplate.buildTemplateWifiWildcard(),
251                 0 /*subId*/,
252                 null /*service*/);
253     }
254 
255     @Override
onActivityCreated(Bundle savedInstanceState)256     public void onActivityCreated(Bundle savedInstanceState) {
257         super.onActivityCreated(savedInstanceState);
258 
259         mWifiTracker = WifiTrackerFactory.create(
260                 getActivity(), this, getSettingsLifecycle(), true, true);
261         mWifiManager = mWifiTracker.getManager();
262 
263         final Activity activity = getActivity();
264         if (activity != null) {
265             mConnectivityManager = getActivity().getSystemService(ConnectivityManager.class);
266         }
267 
268         mConnectListener = new WifiConnectListener(getActivity());
269 
270         mSaveListener = new WifiManager.ActionListener() {
271             @Override
272             public void onSuccess() {
273             }
274 
275             @Override
276             public void onFailure(int reason) {
277                 Activity activity = getActivity();
278                 if (activity != null) {
279                     Toast.makeText(activity,
280                             R.string.wifi_failed_save_message,
281                             Toast.LENGTH_SHORT).show();
282                 }
283             }
284         };
285 
286         mForgetListener = new WifiManager.ActionListener() {
287             @Override
288             public void onSuccess() {
289             }
290 
291             @Override
292             public void onFailure(int reason) {
293                 Activity activity = getActivity();
294                 if (activity != null) {
295                     Toast.makeText(activity,
296                             R.string.wifi_failed_forget_message,
297                             Toast.LENGTH_SHORT).show();
298                 }
299             }
300         };
301 
302         if (savedInstanceState != null) {
303             mDialogMode = savedInstanceState.getInt(SAVE_DIALOG_MODE);
304             if (savedInstanceState.containsKey(SAVE_DIALOG_ACCESS_POINT_STATE)) {
305                 mAccessPointSavedState =
306                         savedInstanceState.getBundle(SAVE_DIALOG_ACCESS_POINT_STATE);
307             }
308         }
309 
310         // if we're supposed to enable/disable the Next button based on our current connection
311         // state, start it off in the right state
312         Intent intent = getActivity().getIntent();
313         mEnableNextOnConnection = intent.getBooleanExtra(EXTRA_ENABLE_NEXT_ON_CONNECT, false);
314 
315         if (mEnableNextOnConnection) {
316             if (hasNextButton()) {
317                 final ConnectivityManager connectivity = (ConnectivityManager)
318                         getActivity().getSystemService(Context.CONNECTIVITY_SERVICE);
319                 if (connectivity != null) {
320                     NetworkInfo info = connectivity.getNetworkInfo(
321                             ConnectivityManager.TYPE_WIFI);
322                     changeNextButtonState(info.isConnected());
323                 }
324             }
325         }
326 
327         registerForContextMenu(getListView());
328         setHasOptionsMenu(true);
329 
330         if (intent.hasExtra(EXTRA_START_CONNECT_SSID)) {
331             mOpenSsid = intent.getStringExtra(EXTRA_START_CONNECT_SSID);
332         }
333     }
334 
335     @Override
onDestroyView()336     public void onDestroyView() {
337         super.onDestroyView();
338 
339         if (mWifiEnabler != null) {
340             mWifiEnabler.teardownSwitchController();
341         }
342     }
343 
344     @Override
onStart()345     public void onStart() {
346         super.onStart();
347 
348         // On/off switch is hidden for Setup Wizard (returns null)
349         mWifiEnabler = createWifiEnabler();
350 
351         if (mIsRestricted) {
352             restrictUi();
353             return;
354         }
355 
356         onWifiStateChanged(mWifiManager.getWifiState());
357     }
358 
restrictUi()359     private void restrictUi() {
360         if (!isUiRestrictedByOnlyAdmin()) {
361             getEmptyTextView().setText(R.string.wifi_empty_list_user_restricted);
362         }
363         getPreferenceScreen().removeAll();
364     }
365 
366     /**
367      * @return new WifiEnabler or null (as overridden by WifiSettingsForSetupWizard)
368      */
createWifiEnabler()369     private WifiEnabler createWifiEnabler() {
370         final SettingsActivity activity = (SettingsActivity) getActivity();
371         return new WifiEnabler(activity, new SwitchBarController(activity.getSwitchBar()),
372                 mMetricsFeatureProvider);
373     }
374 
375     @Override
onResume()376     public void onResume() {
377         final Activity activity = getActivity();
378         super.onResume();
379 
380         // Because RestrictedSettingsFragment's onResume potentially requests authorization,
381         // which changes the restriction state, recalculate it.
382         final boolean alreadyImmutablyRestricted = mIsRestricted;
383         mIsRestricted = isUiRestricted();
384         if (!alreadyImmutablyRestricted && mIsRestricted) {
385             restrictUi();
386         }
387 
388         if (mWifiEnabler != null) {
389             mWifiEnabler.resume(activity);
390         }
391     }
392 
393     @Override
onPause()394     public void onPause() {
395         super.onPause();
396         if (mWifiEnabler != null) {
397             mWifiEnabler.pause();
398         }
399     }
400 
401     @Override
onStop()402     public void onStop() {
403         getView().removeCallbacks(mUpdateAccessPointsRunnable);
404         getView().removeCallbacks(mHideProgressBarRunnable);
405         unregisterCaptivePortalNetworkCallback();
406         super.onStop();
407     }
408 
409     @Override
onActivityResult(int requestCode, int resultCode, Intent data)410     public void onActivityResult(int requestCode, int resultCode, Intent data) {
411         super.onActivityResult(requestCode, resultCode, data);
412 
413         if (requestCode == ADD_NETWORK_REQUEST) {
414             handleAddNetworkRequest(resultCode, data);
415             return;
416         } else if (requestCode == REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER) {
417             if (resultCode == Activity.RESULT_OK) {
418                 if (mDialog != null) {
419                     mDialog.dismiss();
420                 }
421                 mWifiTracker.resumeScanning();
422             }
423             return;
424         }
425 
426         final boolean formerlyRestricted = mIsRestricted;
427         mIsRestricted = isUiRestricted();
428         if (formerlyRestricted && !mIsRestricted
429                 && getPreferenceScreen().getPreferenceCount() == 0) {
430             // De-restrict the ui
431             addPreferences();
432         }
433     }
434 
435     @Override
getMetricsCategory()436     public int getMetricsCategory() {
437         return SettingsEnums.WIFI;
438     }
439 
440     @Override
onSaveInstanceState(Bundle outState)441     public void onSaveInstanceState(Bundle outState) {
442         super.onSaveInstanceState(outState);
443         // If dialog has been shown, save its state.
444         if (mDialog != null) {
445             outState.putInt(SAVE_DIALOG_MODE, mDialogMode);
446             if (mDlgAccessPoint != null) {
447                 mAccessPointSavedState = new Bundle();
448                 mDlgAccessPoint.saveWifiState(mAccessPointSavedState);
449                 outState.putBundle(SAVE_DIALOG_ACCESS_POINT_STATE, mAccessPointSavedState);
450             }
451         }
452     }
453 
454     @Override
onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info)455     public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) {
456         Preference preference = (Preference) view.getTag();
457 
458         if (preference instanceof LongPressAccessPointPreference) {
459             mSelectedAccessPoint =
460                     ((LongPressAccessPointPreference) preference).getAccessPoint();
461             menu.setHeaderTitle(mSelectedAccessPoint.getTitle());
462             if (mSelectedAccessPoint.isConnectable()) {
463                 menu.add(Menu.NONE, MENU_ID_CONNECT, 0 /* order */, R.string.wifi_connect);
464             }
465 
466             WifiConfiguration config = mSelectedAccessPoint.getConfig();
467             // Some configs are ineditable
468             if (WifiUtils.isNetworkLockedDown(getActivity(), config)) {
469                 return;
470             }
471 
472             // "forget" for normal saved network. And "disconnect" for ephemeral network because it
473             // could only be disconnected and be put in blacklists so it won't be used again.
474             if (mSelectedAccessPoint.isSaved() || mSelectedAccessPoint.isEphemeral()) {
475                 final int stringId = mSelectedAccessPoint.isEphemeral() ?
476                     R.string.wifi_disconnect_button_text : R.string.forget;
477                 menu.add(Menu.NONE, MENU_ID_FORGET, 0 /* order */, stringId);
478             }
479 
480             if (mSelectedAccessPoint.isSaved() && !mSelectedAccessPoint.isActive()) {
481                 menu.add(Menu.NONE, MENU_ID_MODIFY, 0 /* order */, R.string.wifi_modify);
482             }
483         }
484     }
485 
486     @Override
onContextItemSelected(MenuItem item)487     public boolean onContextItemSelected(MenuItem item) {
488         if (mSelectedAccessPoint == null) {
489             return super.onContextItemSelected(item);
490         }
491         switch (item.getItemId()) {
492             case MENU_ID_CONNECT: {
493                 boolean isSavedNetwork = mSelectedAccessPoint.isSaved();
494                 if (isSavedNetwork) {
495                     connect(mSelectedAccessPoint.getConfig(), isSavedNetwork);
496                 } else if ((mSelectedAccessPoint.getSecurity() == AccessPoint.SECURITY_NONE) ||
497                         (mSelectedAccessPoint.getSecurity() == AccessPoint.SECURITY_OWE) ||
498                         (mSelectedAccessPoint.getSecurity()
499                                 == AccessPoint.SECURITY_OWE_TRANSITION)) {
500                     /** Bypass dialog for unsecured networks */
501                     mSelectedAccessPoint.generateOpenNetworkConfig();
502                     connect(mSelectedAccessPoint.getConfig(), isSavedNetwork);
503                 } else {
504                     showDialog(mSelectedAccessPoint, WifiConfigUiBase.MODE_CONNECT);
505                 }
506                 return true;
507             }
508             case MENU_ID_FORGET: {
509                 forget();
510                 return true;
511             }
512             case MENU_ID_MODIFY: {
513                 showDialog(mSelectedAccessPoint, WifiConfigUiBase.MODE_MODIFY);
514                 return true;
515             }
516         }
517         return super.onContextItemSelected(item);
518     }
519 
520     @Override
onPreferenceTreeClick(Preference preference)521     public boolean onPreferenceTreeClick(Preference preference) {
522         // If the preference has a fragment set, open that
523         if (preference.getFragment() != null) {
524             preference.setOnPreferenceClickListener(null);
525             return super.onPreferenceTreeClick(preference);
526         }
527 
528         if (preference instanceof LongPressAccessPointPreference) {
529             mSelectedAccessPoint = ((LongPressAccessPointPreference) preference).getAccessPoint();
530             if (mSelectedAccessPoint == null) {
531                 return false;
532             }
533             if (mSelectedAccessPoint.isActive()) {
534                 return super.onPreferenceTreeClick(preference);
535             }
536             /**
537              * Bypass dialog and connect to unsecured networks, or previously connected saved
538              * networks, or Passpoint provided networks.
539              */
540             switch (WifiUtils.getConnectingType(mSelectedAccessPoint)) {
541                 case WifiUtils.CONNECT_TYPE_OSU_PROVISION:
542                     mSelectedAccessPoint.startOsuProvisioning(mConnectListener);
543                     mClickedConnect = true;
544                     break;
545 
546                 case WifiUtils.CONNECT_TYPE_OPEN_NETWORK:
547                     mSelectedAccessPoint.generateOpenNetworkConfig();
548                     connect(mSelectedAccessPoint.getConfig(), mSelectedAccessPoint.isSaved());
549                     break;
550 
551                 case WifiUtils.CONNECT_TYPE_SAVED_NETWORK:
552                     connect(mSelectedAccessPoint.getConfig(), true /* isSavedNetwork */);
553                     break;
554 
555                 default:
556                     showDialog(mSelectedAccessPoint, WifiConfigUiBase.MODE_CONNECT);
557                     break;
558             }
559         } else if (preference == mAddWifiNetworkPreference) {
560             onAddNetworkPressed();
561         } else {
562             return super.onPreferenceTreeClick(preference);
563         }
564         return true;
565     }
566 
showDialog(AccessPoint accessPoint, int dialogMode)567     private void showDialog(AccessPoint accessPoint, int dialogMode) {
568         if (accessPoint != null) {
569             WifiConfiguration config = accessPoint.getConfig();
570             if (WifiUtils.isNetworkLockedDown(getActivity(), config) && accessPoint.isActive()) {
571                 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getActivity(),
572                         RestrictedLockUtilsInternal.getDeviceOwner(getActivity()));
573                 return;
574             }
575         }
576 
577         if (mDialog != null) {
578             removeDialog(WIFI_DIALOG_ID);
579             mDialog = null;
580         }
581 
582         // Save the access point and edit mode
583         mDlgAccessPoint = accessPoint;
584         mDialogMode = dialogMode;
585 
586         showDialog(WIFI_DIALOG_ID);
587     }
588 
589     @Override
onCreateDialog(int dialogId)590     public Dialog onCreateDialog(int dialogId) {
591         switch (dialogId) {
592             case WIFI_DIALOG_ID:
593                 // modify network
594                 if (mDlgAccessPoint == null && mAccessPointSavedState != null) {
595                     // restore AP from save state
596                     mDlgAccessPoint = new AccessPoint(getActivity(), mAccessPointSavedState);
597                     // Reset the saved access point data
598                     mAccessPointSavedState = null;
599                 }
600                 mDialog = WifiDialog
601                         .createModal(getActivity(), this, mDlgAccessPoint, mDialogMode);
602                 mSelectedAccessPoint = mDlgAccessPoint;
603                 return mDialog;
604         }
605         return super.onCreateDialog(dialogId);
606     }
607 
608     @Override
onDialogShowing()609     public void onDialogShowing() {
610         super.onDialogShowing();
611         setOnDismissListener(this);
612     }
613 
614     @Override
onDismiss(DialogInterface dialog)615     public void onDismiss(DialogInterface dialog) {
616         // We don't keep any dialog object when dialog was dismissed.
617         mDialog = null;
618     }
619 
620     @Override
getDialogMetricsCategory(int dialogId)621     public int getDialogMetricsCategory(int dialogId) {
622         switch (dialogId) {
623             case WIFI_DIALOG_ID:
624                 return SettingsEnums.DIALOG_WIFI_AP_EDIT;
625             default:
626                 return 0;
627         }
628     }
629 
630     /**
631      * Called to indicate the list of AccessPoints has been updated and
632      * getAccessPoints should be called to get the latest information.
633      */
634     @Override
onAccessPointsChanged()635     public void onAccessPointsChanged() {
636         Log.d(TAG, "onAccessPointsChanged (WifiTracker) callback initiated");
637         updateAccessPointsDelayed();
638     }
639 
640     /**
641      * Updates access points from {@link WifiManager#getScanResults()}. Adds a delay to have
642      * progress bar displayed before starting to modify APs.
643      */
updateAccessPointsDelayed()644     private void updateAccessPointsDelayed() {
645         // Safeguard from some delayed event handling
646         if (getActivity() != null && !mIsRestricted && mWifiManager.isWifiEnabled()) {
647             final View view = getView();
648             final Handler handler = view.getHandler();
649             if (handler != null && handler.hasCallbacks(mUpdateAccessPointsRunnable)) {
650                 return;
651             }
652             setProgressBarVisible(true);
653             view.postDelayed(mUpdateAccessPointsRunnable, 300 /* delay milliseconds */);
654         }
655     }
656 
657     /** Called when the state of Wifi has changed. */
658     @Override
onWifiStateChanged(int state)659     public void onWifiStateChanged(int state) {
660         if (mIsRestricted) {
661             return;
662         }
663 
664         final int wifiState = mWifiManager.getWifiState();
665         switch (wifiState) {
666             case WifiManager.WIFI_STATE_ENABLED:
667                 updateAccessPointPreferences();
668                 break;
669 
670             case WifiManager.WIFI_STATE_ENABLING:
671                 removeConnectedAccessPointPreference();
672                 removeAccessPointPreference();
673                 addMessagePreference(R.string.wifi_starting);
674                 setProgressBarVisible(true);
675                 break;
676 
677             case WifiManager.WIFI_STATE_DISABLING:
678                 removeConnectedAccessPointPreference();
679                 removeAccessPointPreference();
680                 addMessagePreference(R.string.wifi_stopping);
681                 break;
682 
683             case WifiManager.WIFI_STATE_DISABLED:
684                 setOffMessage();
685                 setAdditionalSettingsSummaries();
686                 setProgressBarVisible(false);
687                 break;
688         }
689     }
690 
691     /**
692      * Called when the connection state of wifi has changed.
693      */
694     @Override
onConnectedChanged()695     public void onConnectedChanged() {
696         changeNextButtonState(mWifiTracker.isConnected());
697     }
698 
699     /** Helper method to return whether an AccessPoint is disabled due to a wrong password */
isDisabledByWrongPassword(AccessPoint accessPoint)700     private static boolean isDisabledByWrongPassword(AccessPoint accessPoint) {
701         WifiConfiguration config = accessPoint.getConfig();
702         if (config == null) {
703             return false;
704         }
705         WifiConfiguration.NetworkSelectionStatus networkStatus =
706                 config.getNetworkSelectionStatus();
707         if (networkStatus == null || networkStatus.isNetworkEnabled()) {
708             return false;
709         }
710         int reason = networkStatus.getNetworkSelectionDisableReason();
711         return WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD == reason;
712     }
713 
updateAccessPointPreferences()714     private void updateAccessPointPreferences() {
715         // in case state has changed
716         if (!mWifiManager.isWifiEnabled()) {
717             return;
718         }
719         // AccessPoints are sorted by the WifiTracker
720         final List<AccessPoint> accessPoints = mWifiTracker.getAccessPoints();
721         if (isVerboseLoggingEnabled()) {
722             Log.i(TAG, "updateAccessPoints called for: " + accessPoints);
723         }
724 
725         boolean hasAvailableAccessPoints = false;
726         mStatusMessagePreference.setVisible(false);
727         mConnectedAccessPointPreferenceCategory.setVisible(true);
728         mAccessPointsPreferenceCategory.setVisible(true);
729 
730         cacheRemoveAllPrefs(mAccessPointsPreferenceCategory);
731 
732         int index =
733                 configureConnectedAccessPointPreferenceCategory(accessPoints) ? 1 : 0;
734         int numAccessPoints = accessPoints.size();
735         for (; index < numAccessPoints; index++) {
736             AccessPoint accessPoint = accessPoints.get(index);
737             // Ignore access points that are out of range.
738             if (accessPoint.isReachable()) {
739                 String key = accessPoint.getKey();
740                 hasAvailableAccessPoints = true;
741                 LongPressAccessPointPreference pref =
742                         (LongPressAccessPointPreference) getCachedPreference(key);
743                 if (pref != null) {
744                     pref.setOrder(index);
745                     continue;
746                 }
747                 LongPressAccessPointPreference preference =
748                         createLongPressAccessPointPreference(accessPoint);
749                 preference.setKey(key);
750                 preference.setOrder(index);
751                 if (mOpenSsid != null && mOpenSsid.equals(accessPoint.getSsidStr())
752                         && (accessPoint.getSecurity() != AccessPoint.SECURITY_NONE &&
753                         accessPoint.getSecurity() != AccessPoint.SECURITY_OWE &&
754                         accessPoint.getSecurity() != AccessPoint.SECURITY_OWE_TRANSITION)) {
755                     if (!accessPoint.isSaved() || isDisabledByWrongPassword(accessPoint)) {
756                         onPreferenceTreeClick(preference);
757                         mOpenSsid = null;
758                     }
759                 }
760                 mAccessPointsPreferenceCategory.addPreference(preference);
761                 accessPoint.setListener(WifiSettings.this);
762                 preference.refresh();
763             }
764         }
765         removeCachedPrefs(mAccessPointsPreferenceCategory);
766         mAddWifiNetworkPreference.setOrder(index);
767         mAccessPointsPreferenceCategory.addPreference(mAddWifiNetworkPreference);
768         setAdditionalSettingsSummaries();
769 
770         if (!hasAvailableAccessPoints) {
771             setProgressBarVisible(true);
772             Preference pref = new Preference(getPrefContext());
773             pref.setSelectable(false);
774             pref.setSummary(R.string.wifi_empty_list_wifi_on);
775             pref.setOrder(index++);
776             pref.setKey(PREF_KEY_EMPTY_WIFI_LIST);
777             mAccessPointsPreferenceCategory.addPreference(pref);
778         } else {
779             // Continuing showing progress bar for an additional delay to overlap with animation
780             getView().postDelayed(mHideProgressBarRunnable, 1700 /* delay millis */);
781         }
782     }
783 
784     @NonNull
createLongPressAccessPointPreference( AccessPoint accessPoint)785     private LongPressAccessPointPreference createLongPressAccessPointPreference(
786             AccessPoint accessPoint) {
787         return new LongPressAccessPointPreference(accessPoint, getPrefContext(), mUserBadgeCache,
788                 false /* forSavedNetworks */, R.drawable.ic_wifi_signal_0, this);
789     }
790 
791     @NonNull
792     @VisibleForTesting
createConnectedAccessPointPreference( AccessPoint accessPoint, Context context)793     ConnectedAccessPointPreference createConnectedAccessPointPreference(
794             AccessPoint accessPoint, Context context) {
795         return new ConnectedAccessPointPreference(accessPoint, context, mUserBadgeCache,
796                 R.drawable.ic_wifi_signal_0, false /* forSavedNetworks */, this);
797     }
798 
799     /**
800      * Configure the ConnectedAccessPointPreferenceCategory and return true if the Category was
801      * shown.
802      */
configureConnectedAccessPointPreferenceCategory( List<AccessPoint> accessPoints)803     private boolean configureConnectedAccessPointPreferenceCategory(
804             List<AccessPoint> accessPoints) {
805         if (accessPoints.size() == 0) {
806             removeConnectedAccessPointPreference();
807             return false;
808         }
809 
810         AccessPoint connectedAp = accessPoints.get(0);
811         if (!connectedAp.isActive()) {
812             removeConnectedAccessPointPreference();
813             return false;
814         }
815 
816         // Is the preference category empty?
817         if (mConnectedAccessPointPreferenceCategory.getPreferenceCount() == 0) {
818             addConnectedAccessPointPreference(connectedAp);
819             return true;
820         }
821 
822         // Is the previous currently connected SSID different from the new one?
823         ConnectedAccessPointPreference preference =
824                 (ConnectedAccessPointPreference)
825                         (mConnectedAccessPointPreferenceCategory.getPreference(0));
826         // The AccessPoints need to be the same reference to ensure that updates are reflected
827         // in the UI.
828         if (preference.getAccessPoint() != connectedAp) {
829             removeConnectedAccessPointPreference();
830             addConnectedAccessPointPreference(connectedAp);
831             return true;
832         }
833 
834         // Else same AP is connected, simply refresh the connected access point preference
835         // (first and only access point in this category).
836         preference.refresh();
837         // Update any potential changes to the connected network and ensure that the callback is
838         // registered after an onStop lifecycle event.
839         registerCaptivePortalNetworkCallback(getCurrentWifiNetwork(), preference);
840         return true;
841     }
842 
843     /**
844      * Creates a Preference for the given {@link AccessPoint} and adds it to the
845      * {@link #mConnectedAccessPointPreferenceCategory}.
846      */
addConnectedAccessPointPreference(AccessPoint connectedAp)847     private void addConnectedAccessPointPreference(AccessPoint connectedAp) {
848         final ConnectedAccessPointPreference pref =
849                 createConnectedAccessPointPreference(connectedAp, getPrefContext());
850         registerCaptivePortalNetworkCallback(getCurrentWifiNetwork(), pref);
851 
852         // Launch details page or captive portal on click.
853         pref.setOnPreferenceClickListener(
854                 preference -> {
855                     pref.getAccessPoint().saveWifiState(pref.getExtras());
856                     if (mCaptivePortalNetworkCallback != null
857                             && mCaptivePortalNetworkCallback.isCaptivePortal()) {
858                         mConnectivityManager.startCaptivePortalApp(
859                                 mCaptivePortalNetworkCallback.getNetwork());
860                     } else {
861                         launchNetworkDetailsFragment(pref);
862                     }
863                     return true;
864                 });
865 
866         pref.setOnGearClickListener(
867                 preference -> {
868                     pref.getAccessPoint().saveWifiState(pref.getExtras());
869                     launchNetworkDetailsFragment(pref);
870                 });
871 
872         pref.refresh();
873 
874         mConnectedAccessPointPreferenceCategory.addPreference(pref);
875         mConnectedAccessPointPreferenceCategory.setVisible(true);
876         if (mClickedConnect) {
877             mClickedConnect = false;
878             scrollToPreference(mConnectedAccessPointPreferenceCategory);
879         }
880     }
881 
registerCaptivePortalNetworkCallback( Network wifiNetwork, ConnectedAccessPointPreference pref)882     private void registerCaptivePortalNetworkCallback(
883             Network wifiNetwork, ConnectedAccessPointPreference pref) {
884         if (wifiNetwork == null || pref == null) {
885             Log.w(TAG, "Network or Preference were null when registering callback.");
886             return;
887         }
888 
889         if (mCaptivePortalNetworkCallback != null
890                 && mCaptivePortalNetworkCallback.isSameNetworkAndPreference(wifiNetwork, pref)) {
891             return;
892         }
893 
894         unregisterCaptivePortalNetworkCallback();
895 
896         mCaptivePortalNetworkCallback = new CaptivePortalNetworkCallback(wifiNetwork, pref);
897         mConnectivityManager.registerNetworkCallback(
898                 new NetworkRequest.Builder()
899                         .clearCapabilities()
900                         .addTransportType(TRANSPORT_WIFI)
901                         .build(),
902                 mCaptivePortalNetworkCallback,
903                 new Handler(Looper.getMainLooper()));
904     }
905 
unregisterCaptivePortalNetworkCallback()906     private void unregisterCaptivePortalNetworkCallback() {
907         if (mCaptivePortalNetworkCallback != null) {
908             try {
909                 mConnectivityManager.unregisterNetworkCallback(mCaptivePortalNetworkCallback);
910             } catch (RuntimeException e) {
911                 Log.e(TAG, "Unregistering CaptivePortalNetworkCallback failed.", e);
912             }
913             mCaptivePortalNetworkCallback = null;
914         }
915     }
916 
launchAddNetworkFragment()917     private void launchAddNetworkFragment() {
918         new SubSettingLauncher(getContext())
919                 .setTitleRes(R.string.wifi_add_network)
920                 .setDestination(AddNetworkFragment.class.getName())
921                 .setSourceMetricsCategory(getMetricsCategory())
922                 .setResultListener(this, ADD_NETWORK_REQUEST)
923                 .launch();
924     }
925 
launchNetworkDetailsFragment(ConnectedAccessPointPreference pref)926     private void launchNetworkDetailsFragment(ConnectedAccessPointPreference pref) {
927         final AccessPoint accessPoint = pref.getAccessPoint();
928         final Context context = getContext();
929         final CharSequence title =
930                 FeatureFlagUtils.isEnabled(context, FeatureFlags.WIFI_DETAILS_DATAUSAGE_HEADER)
931                         ? accessPoint.getTitle()
932                         : context.getText(R.string.pref_title_network_details);
933 
934         new SubSettingLauncher(getContext())
935                 .setTitleText(title)
936                 .setDestination(WifiNetworkDetailsFragment.class.getName())
937                 .setArguments(pref.getExtras())
938                 .setSourceMetricsCategory(getMetricsCategory())
939                 .launch();
940     }
941 
getCurrentWifiNetwork()942     private Network getCurrentWifiNetwork() {
943         return mWifiManager != null ? mWifiManager.getCurrentNetwork() : null;
944     }
945 
946     /** Removes all preferences and hide the {@link #mConnectedAccessPointPreferenceCategory}. */
removeConnectedAccessPointPreference()947     private void removeConnectedAccessPointPreference() {
948         mConnectedAccessPointPreferenceCategory.removeAll();
949         mConnectedAccessPointPreferenceCategory.setVisible(false);
950         unregisterCaptivePortalNetworkCallback();
951     }
952 
removeAccessPointPreference()953     private void removeAccessPointPreference() {
954         mAccessPointsPreferenceCategory.removeAll();
955         mAccessPointsPreferenceCategory.setVisible(false);
956     }
957 
958     @VisibleForTesting
setAdditionalSettingsSummaries()959     void setAdditionalSettingsSummaries() {
960         mConfigureWifiSettingsPreference.setSummary(getString(
961                 isWifiWakeupEnabled()
962                         ? R.string.wifi_configure_settings_preference_summary_wakeup_on
963                         : R.string.wifi_configure_settings_preference_summary_wakeup_off));
964 
965         final List<AccessPoint> savedNetworks =
966                 WifiSavedConfigUtils.getAllConfigs(getContext(), mWifiManager);
967         final int numSavedNetworks = (savedNetworks != null) ? savedNetworks.size() : 0;
968         mSavedNetworksPreference.setVisible(numSavedNetworks > 0);
969         if (numSavedNetworks > 0) {
970             mSavedNetworksPreference.setSummary(
971                     getSavedNetworkSettingsSummaryText(savedNetworks, numSavedNetworks));
972         }
973     }
974 
getSavedNetworkSettingsSummaryText( List<AccessPoint> savedNetworks, int numSavedNetworks)975     private String getSavedNetworkSettingsSummaryText(
976             List<AccessPoint> savedNetworks, int numSavedNetworks) {
977         int numSavedPasspointNetworks = 0;
978         for (AccessPoint savedNetwork : savedNetworks) {
979             if (savedNetwork.isPasspointConfig() || savedNetwork.isPasspoint()) {
980                 numSavedPasspointNetworks++;
981             }
982         }
983         final int numSavedNormalNetworks = numSavedNetworks - numSavedPasspointNetworks;
984 
985         if (numSavedNetworks == numSavedNormalNetworks) {
986             return getResources().getQuantityString(R.plurals.wifi_saved_access_points_summary,
987                     numSavedNormalNetworks, numSavedNormalNetworks);
988         } else if (numSavedNetworks == numSavedPasspointNetworks) {
989             return getResources().getQuantityString(
990                     R.plurals.wifi_saved_passpoint_access_points_summary,
991                     numSavedPasspointNetworks, numSavedPasspointNetworks);
992         } else {
993             return getResources().getQuantityString(R.plurals.wifi_saved_all_access_points_summary,
994                     numSavedNetworks, numSavedNetworks);
995         }
996     }
997 
isWifiWakeupEnabled()998     private boolean isWifiWakeupEnabled() {
999         final Context context = getContext();
1000         final PowerManager powerManager = context.getSystemService(PowerManager.class);
1001         final ContentResolver contentResolver = context.getContentResolver();
1002         return Settings.Global.getInt(contentResolver,
1003                 Settings.Global.WIFI_WAKEUP_ENABLED, 0) == 1
1004                 && Settings.Global.getInt(contentResolver,
1005                 Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 1
1006                 && Settings.Global.getInt(contentResolver,
1007                 Settings.Global.AIRPLANE_MODE_ON, 0) == 0
1008                 && !powerManager.isPowerSaveMode();
1009     }
1010 
setOffMessage()1011     private void setOffMessage() {
1012         final CharSequence title = getText(R.string.wifi_empty_list_wifi_off);
1013         // Don't use WifiManager.isScanAlwaysAvailable() to check the Wi-Fi scanning mode. Instead,
1014         // read the system settings directly. Because when the device is in Airplane mode, even if
1015         // Wi-Fi scanning mode is on, WifiManager.isScanAlwaysAvailable() still returns "off".
1016         final boolean wifiScanningMode = Settings.Global.getInt(getActivity().getContentResolver(),
1017                 Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 0) == 1;
1018         final CharSequence description = wifiScanningMode ? getText(R.string.wifi_scan_notify_text)
1019                 : getText(R.string.wifi_scan_notify_text_scanning_off);
1020         final LinkifyUtils.OnClickListener clickListener =
1021                 () -> new SubSettingLauncher(getContext())
1022                         .setDestination(ScanningSettings.class.getName())
1023                         .setTitleRes(R.string.location_scanning_screen_title)
1024                         .setSourceMetricsCategory(getMetricsCategory())
1025                         .launch();
1026         mStatusMessagePreference.setText(title, description, clickListener);
1027         removeConnectedAccessPointPreference();
1028         removeAccessPointPreference();
1029         mStatusMessagePreference.setVisible(true);
1030     }
1031 
addMessagePreference(int messageId)1032     private void addMessagePreference(int messageId) {
1033         mStatusMessagePreference.setTitle(messageId);
1034         mStatusMessagePreference.setVisible(true);
1035 
1036     }
1037 
setProgressBarVisible(boolean visible)1038     protected void setProgressBarVisible(boolean visible) {
1039         if (mProgressHeader != null) {
1040             mProgressHeader.setVisibility(visible ? View.VISIBLE : View.GONE);
1041         }
1042     }
1043 
1044     /**
1045      * Renames/replaces "Next" button when appropriate. "Next" button usually exists in
1046      * Wifi setup screens, not in usual wifi settings screen.
1047      *
1048      * @param enabled true when the device is connected to a wifi network.
1049      */
changeNextButtonState(boolean enabled)1050     private void changeNextButtonState(boolean enabled) {
1051         if (mEnableNextOnConnection && hasNextButton()) {
1052             getNextButton().setEnabled(enabled);
1053         }
1054     }
1055 
1056     @Override
onForget(WifiDialog dialog)1057     public void onForget(WifiDialog dialog) {
1058         forget();
1059     }
1060 
1061     @Override
onSubmit(WifiDialog dialog)1062     public void onSubmit(WifiDialog dialog) {
1063         if (mDialog != null) {
1064             submit(mDialog.getController());
1065         }
1066     }
1067 
1068     @Override
onScan(WifiDialog dialog, String ssid)1069     public void onScan(WifiDialog dialog, String ssid) {
1070         // Launch QR code scanner to join a network.
1071         startActivityForResult(WifiDppUtils.getEnrolleeQrCodeScannerIntent(ssid),
1072                 REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER);
1073     }
1074 
submit(WifiConfigController configController)1075     /* package */ void submit(WifiConfigController configController) {
1076 
1077         final WifiConfiguration config = configController.getConfig();
1078 
1079         if (config == null) {
1080             if (mSelectedAccessPoint != null
1081                     && mSelectedAccessPoint.isSaved()) {
1082                 connect(mSelectedAccessPoint.getConfig(), true /* isSavedNetwork */);
1083             }
1084         } else if (configController.getMode() == WifiConfigUiBase.MODE_MODIFY) {
1085             mWifiManager.save(config, mSaveListener);
1086         } else {
1087             mWifiManager.save(config, mSaveListener);
1088             if (mSelectedAccessPoint != null) { // Not an "Add network"
1089                 connect(config, false /* isSavedNetwork */);
1090             }
1091         }
1092 
1093         mWifiTracker.resumeScanning();
1094     }
1095 
forget()1096     /* package */ void forget() {
1097         mMetricsFeatureProvider.action(getActivity(), SettingsEnums.ACTION_WIFI_FORGET);
1098         if (!mSelectedAccessPoint.isSaved()) {
1099             if (mSelectedAccessPoint.getNetworkInfo() != null &&
1100                     mSelectedAccessPoint.getNetworkInfo().getState() != State.DISCONNECTED) {
1101                 // Network is active but has no network ID - must be ephemeral.
1102                 mWifiManager.disableEphemeralNetwork(
1103                         AccessPoint.convertToQuotedString(mSelectedAccessPoint.getSsidStr()));
1104             } else {
1105                 // Should not happen, but a monkey seems to trigger it
1106                 Log.e(TAG, "Failed to forget invalid network " + mSelectedAccessPoint.getConfig());
1107                 return;
1108             }
1109         } else if (mSelectedAccessPoint.getConfig().isPasspoint()) {
1110             try {
1111                 mWifiManager.removePasspointConfiguration(mSelectedAccessPoint.getConfig().FQDN);
1112             } catch (IllegalArgumentException e) {
1113                 Log.e(TAG, "Failed to remove Passpoint configuration with error: " + e);
1114                 return;
1115             }
1116         } else {
1117             mWifiManager.forget(mSelectedAccessPoint.getConfig().networkId, mForgetListener);
1118         }
1119 
1120         mWifiTracker.resumeScanning();
1121 
1122         // We need to rename/replace "Next" button in wifi setup context.
1123         changeNextButtonState(false);
1124     }
1125 
connect(final WifiConfiguration config, boolean isSavedNetwork)1126     protected void connect(final WifiConfiguration config, boolean isSavedNetwork) {
1127         // Log subtype if configuration is a saved network.
1128         mMetricsFeatureProvider.action(getContext(), SettingsEnums.ACTION_WIFI_CONNECT,
1129                 isSavedNetwork);
1130         mWifiManager.connect(config, mConnectListener);
1131         mClickedConnect = true;
1132     }
1133 
connect(final int networkId, boolean isSavedNetwork)1134     protected void connect(final int networkId, boolean isSavedNetwork) {
1135         // Log subtype if configuration is a saved network.
1136         mMetricsFeatureProvider.action(getActivity(), SettingsEnums.ACTION_WIFI_CONNECT,
1137                 isSavedNetwork);
1138         mWifiManager.connect(networkId, mConnectListener);
1139     }
1140 
1141     @VisibleForTesting
handleAddNetworkRequest(int result, Intent data)1142     void handleAddNetworkRequest(int result, Intent data) {
1143         if (result == Activity.RESULT_OK) {
1144             handleAddNetworkSubmitEvent(data);
1145         }
1146         mWifiTracker.resumeScanning();
1147     }
1148 
handleAddNetworkSubmitEvent(Intent data)1149     private void handleAddNetworkSubmitEvent(Intent data) {
1150         final WifiConfiguration wifiConfiguration = data.getParcelableExtra(
1151                 AddNetworkFragment.WIFI_CONFIG_KEY);
1152         if (wifiConfiguration != null) {
1153             mWifiManager.save(wifiConfiguration, mSaveListener);
1154         }
1155     }
1156 
1157     /**
1158      * Called when "add network" button is pressed.
1159      */
onAddNetworkPressed()1160     private void onAddNetworkPressed() {
1161         // No exact access point is selected.
1162         mSelectedAccessPoint = null;
1163         launchAddNetworkFragment();
1164     }
1165 
1166     @Override
getHelpResource()1167     public int getHelpResource() {
1168         return R.string.help_url_wifi;
1169     }
1170 
1171     @Override
onAccessPointChanged(final AccessPoint accessPoint)1172     public void onAccessPointChanged(final AccessPoint accessPoint) {
1173         Log.d(TAG, "onAccessPointChanged (singular) callback initiated");
1174         View view = getView();
1175         if (view != null) {
1176             view.post(new Runnable() {
1177                 @Override
1178                 public void run() {
1179                     Object tag = accessPoint.getTag();
1180                     if (tag != null) {
1181                         ((AccessPointPreference) tag).refresh();
1182                     }
1183                 }
1184             });
1185         }
1186     }
1187 
1188     @Override
onLevelChanged(AccessPoint accessPoint)1189     public void onLevelChanged(AccessPoint accessPoint) {
1190         ((AccessPointPreference) accessPoint.getTag()).onLevelChanged();
1191     }
1192 
1193     public static final SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
1194             new BaseSearchIndexProvider() {
1195                 @Override
1196                 public List<SearchIndexableRaw> getRawDataToIndex(Context context,
1197                         boolean enabled) {
1198                     final List<SearchIndexableRaw> result = new ArrayList<>();
1199                     final Resources res = context.getResources();
1200 
1201                     // Add fragment title if we are showing this fragment
1202                     if (res.getBoolean(R.bool.config_show_wifi_settings)) {
1203                         SearchIndexableRaw data = new SearchIndexableRaw(context);
1204                         data.title = res.getString(R.string.wifi_settings);
1205                         data.screenTitle = res.getString(R.string.wifi_settings);
1206                         data.keywords = res.getString(R.string.keywords_wifi);
1207                         data.key = DATA_KEY_REFERENCE;
1208                         result.add(data);
1209                     }
1210 
1211                     return result;
1212                 }
1213             };
1214 
1215     private static class SummaryProvider
1216             implements SummaryLoader.SummaryProvider, OnSummaryChangeListener {
1217 
1218         private final Context mContext;
1219         private final SummaryLoader mSummaryLoader;
1220 
1221         @VisibleForTesting
1222         WifiSummaryUpdater mSummaryHelper;
1223 
SummaryProvider(Context context, SummaryLoader summaryLoader)1224         public SummaryProvider(Context context, SummaryLoader summaryLoader) {
1225             mContext = context;
1226             mSummaryLoader = summaryLoader;
1227             mSummaryHelper = new WifiSummaryUpdater(mContext, this);
1228         }
1229 
1230 
1231         @Override
setListening(boolean listening)1232         public void setListening(boolean listening) {
1233             mSummaryHelper.register(listening);
1234         }
1235 
1236         @Override
onSummaryChanged(String summary)1237         public void onSummaryChanged(String summary) {
1238             mSummaryLoader.setSummary(this, summary);
1239         }
1240     }
1241 
1242     public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY
1243             = new SummaryLoader.SummaryProviderFactory() {
1244         @Override
1245         public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
1246                 SummaryLoader summaryLoader) {
1247             return new SummaryProvider(activity, summaryLoader);
1248         }
1249     };
1250 }
1251