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