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