1 /*
2  * Copyright (C) 2020 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.network;
18 
19 import static android.net.wifi.WifiConfiguration.NetworkSelectionStatus.NETWORK_SELECTION_ENABLED;
20 import static android.os.UserManager.DISALLOW_CONFIG_WIFI;
21 
22 import static com.android.settings.Settings.WifiSettingsActivity;
23 
24 import android.app.Activity;
25 import android.app.Dialog;
26 import android.app.settings.SettingsEnums;
27 import android.content.ActivityNotFoundException;
28 import android.content.ContentResolver;
29 import android.content.Context;
30 import android.content.DialogInterface;
31 import android.content.Intent;
32 import android.location.LocationManager;
33 import android.net.NetworkTemplate;
34 import android.net.wifi.WifiConfiguration;
35 import android.net.wifi.WifiManager;
36 import android.os.Bundle;
37 import android.os.Handler;
38 import android.os.PowerManager;
39 import android.os.UserManager;
40 import android.provider.Settings;
41 import android.telephony.TelephonyManager;
42 import android.text.TextUtils;
43 import android.util.EventLog;
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.MenuInflater;
50 import android.view.MenuItem;
51 import android.view.View;
52 import android.widget.Toast;
53 
54 import androidx.annotation.VisibleForTesting;
55 import androidx.appcompat.app.AlertDialog;
56 import androidx.fragment.app.Fragment;
57 import androidx.preference.Preference;
58 import androidx.preference.PreferenceCategory;
59 import androidx.preference.PreferenceScreen;
60 import androidx.recyclerview.widget.RecyclerView;
61 
62 import com.android.settings.AirplaneModeEnabler;
63 import com.android.settings.R;
64 import com.android.settings.RestrictedSettingsFragment;
65 import com.android.settings.core.FeatureFlags;
66 import com.android.settings.core.SubSettingLauncher;
67 import com.android.settings.datausage.DataUsagePreference;
68 import com.android.settings.datausage.DataUsageUtils;
69 import com.android.settings.location.WifiScanningFragment;
70 import com.android.settings.search.BaseSearchIndexProvider;
71 import com.android.settings.utils.AnnotationSpan;
72 import com.android.settings.wifi.AddNetworkFragment;
73 import com.android.settings.wifi.AddWifiNetworkPreference;
74 import com.android.settings.wifi.ConfigureWifiEntryFragment;
75 import com.android.settings.wifi.ConnectedWifiEntryPreference;
76 import com.android.settings.wifi.WifiConfigUiBase2;
77 import com.android.settings.wifi.WifiConnectListener;
78 import com.android.settings.wifi.WifiDialog2;
79 import com.android.settings.wifi.WifiPickerTrackerHelper;
80 import com.android.settings.wifi.WifiUtils;
81 import com.android.settings.wifi.details.WifiNetworkDetailsFragment;
82 import com.android.settings.wifi.dpp.WifiDppUtils;
83 import com.android.settingslib.HelpUtils;
84 import com.android.settingslib.RestrictedLockUtils;
85 import com.android.settingslib.RestrictedLockUtilsInternal;
86 import com.android.settingslib.search.Indexable;
87 import com.android.settingslib.search.SearchIndexable;
88 import com.android.settingslib.utils.ThreadUtils;
89 import com.android.settingslib.widget.FooterPreference;
90 import com.android.settingslib.widget.LayoutPreference;
91 import com.android.settingslib.wifi.LongPressWifiEntryPreference;
92 import com.android.settingslib.wifi.WifiSavedConfigUtils;
93 import com.android.wifitrackerlib.WifiEntry;
94 import com.android.wifitrackerlib.WifiEntry.ConnectCallback;
95 import com.android.wifitrackerlib.WifiPickerTracker;
96 
97 import java.util.List;
98 import java.util.Optional;
99 
100 /**
101  * UI for Mobile network and Wi-Fi network settings.
102  *
103  * TODO(b/167474581): Define the intent android.settings.NETWORK_PROVIDER_SETTINGS in Settings.java.
104  */
105 @SearchIndexable
106 public class NetworkProviderSettings extends RestrictedSettingsFragment
107         implements Indexable, WifiPickerTracker.WifiPickerTrackerCallback,
108         WifiDialog2.WifiDialog2Listener, DialogInterface.OnDismissListener,
109         AirplaneModeEnabler.OnAirplaneModeChangedListener, InternetUpdater.InternetChangeListener {
110 
111     public static final String ACTION_NETWORK_PROVIDER_SETTINGS =
112             "android.settings.NETWORK_PROVIDER_SETTINGS";
113 
114     private static final String TAG = "NetworkProviderSettings";
115     // IDs of context menu
116     static final int MENU_ID_CONNECT = Menu.FIRST + 1;
117     @VisibleForTesting
118     static final int MENU_ID_DISCONNECT = Menu.FIRST + 2;
119     @VisibleForTesting
120     static final int MENU_ID_FORGET = Menu.FIRST + 3;
121     static final int MENU_ID_MODIFY = Menu.FIRST + 4;
122     static final int MENU_FIX_CONNECTIVITY = Menu.FIRST + 5;
123     static final int MENU_ID_SHARE = Menu.FIRST + 6;
124 
125     @VisibleForTesting
126     static final int ADD_NETWORK_REQUEST = 2;
127     static final int CONFIG_NETWORK_REQUEST = 3;
128     static final int MANAGE_SUBSCRIPTION = 4;
129 
130     private static final String PREF_KEY_AIRPLANE_MODE_MSG = "airplane_mode_message";
131     private static final String PREF_KEY_EMPTY_WIFI_LIST = "wifi_empty_list";
132     // TODO(b/70983952): Rename these to use WifiEntry instead of AccessPoint.
133     @VisibleForTesting
134     static final String PREF_KEY_CONNECTED_ACCESS_POINTS = "connected_access_point";
135     @VisibleForTesting
136     static final String PREF_KEY_FIRST_ACCESS_POINTS = "first_access_points";
137     private static final String PREF_KEY_ACCESS_POINTS = "access_points";
138     private static final String PREF_KEY_CONFIGURE_NETWORK_SETTINGS = "configure_network_settings";
139     private static final String PREF_KEY_SAVED_NETWORKS = "saved_networks";
140     @VisibleForTesting
141     static final String PREF_KEY_DATA_USAGE = "non_carrier_data_usage";
142     private static final String PREF_KEY_RESET_INTERNET = "resetting_your_internet";
143     private static final String PREF_KEY_WIFI_STATUS_MESSAGE = "wifi_status_message_footer";
144 
145     private static final int REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER = 0;
146 
147     public static final int WIFI_DIALOG_ID = 1;
148 
149     // Instance state keys
150     private static final String SAVE_DIALOG_MODE = "dialog_mode";
151     private static final String SAVE_DIALOG_WIFIENTRY_KEY = "wifi_ap_key";
152 
153     // Cache at onCreateContextMenu and use at onContextItemSelected. Don't use it in other methods.
154     private WifiEntry mSelectedWifiEntry;
155 
156     // Save the dialog details
157     private int mDialogMode;
158     private String mDialogWifiEntryKey;
159     private WifiEntry mDialogWifiEntry;
160 
161     // This boolean extra specifies whether to enable the Next button when connected. Used by
162     // account creation outside of setup wizard.
163     private static final String EXTRA_ENABLE_NEXT_ON_CONNECT = "wifi_enable_next_on_connect";
164 
165     // Enable the Next button when a Wi-Fi network is connected.
166     private boolean mEnableNextOnConnection;
167 
168     // This string extra specifies a network to open the connect dialog on, so the user can enter
169     // network credentials.  This is used by quick settings for secured networks, among other
170     // things.
171     private static final String EXTRA_START_CONNECT_SSID = "wifi_start_connect_ssid";
172     private String mOpenSsid;
173 
isVerboseLoggingEnabled()174     private static boolean isVerboseLoggingEnabled() {
175         return WifiPickerTracker.isVerboseLoggingEnabled();
176     }
177 
178     private boolean mIsViewLoading;
179     @VisibleForTesting
180     final Runnable mRemoveLoadingRunnable = () -> {
181         if (mIsViewLoading) {
182             setLoading(false, false);
183             mIsViewLoading = false;
184         }
185     };
186 
187     private boolean mIsWifiEntryListStale = true;
188     @VisibleForTesting
189     final Runnable mUpdateWifiEntryPreferencesRunnable = () -> {
190         updateWifiEntryPreferences();
191         getView().postDelayed(mRemoveLoadingRunnable, 10);
192     };
193     @VisibleForTesting
194     final Runnable mHideProgressBarRunnable = () -> {
195         setProgressBarVisible(false);
196     };
197 
198     protected WifiManager mWifiManager;
199     private WifiManager.ActionListener mConnectListener;
200     private WifiManager.ActionListener mSaveListener;
201     private WifiManager.ActionListener mForgetListener;
202 
203     protected InternetResetHelper mInternetResetHelper;
204 
205     /**
206      * The state of {@link #isUiRestricted()} at {@link #onCreate(Bundle)}}. This is necessary to
207      * ensure that behavior is consistent if {@link #isUiRestricted()} changes. It could be changed
208      * by the Test DPC tool in AFW mode.
209      */
210     protected boolean mIsRestricted;
211     @VisibleForTesting
212     boolean mIsAdmin = true;
213 
214     @VisibleForTesting
215     AirplaneModeEnabler mAirplaneModeEnabler;
216     @VisibleForTesting
217     WifiPickerTracker mWifiPickerTracker;
218     private WifiPickerTrackerHelper mWifiPickerTrackerHelper;
219     @VisibleForTesting
220     InternetUpdater mInternetUpdater;
221 
222     private WifiDialog2 mDialog;
223 
224     @VisibleForTesting
225     PreferenceCategory mConnectedWifiEntryPreferenceCategory;
226     @VisibleForTesting
227     PreferenceCategory mFirstWifiEntryPreferenceCategory;
228     @VisibleForTesting
229     PreferenceCategory mWifiEntryPreferenceCategory;
230     @VisibleForTesting
231     AddWifiNetworkPreference mAddWifiNetworkPreference;
232     private WifiSwitchPreferenceController mWifiSwitchPreferenceController;
233     @VisibleForTesting
234     Preference mConfigureWifiSettingsPreference;
235     @VisibleForTesting
236     Preference mSavedNetworksPreference;
237     @VisibleForTesting
238     DataUsagePreference mDataUsagePreference;
239     @VisibleForTesting
240     Preference mAirplaneModeMsgPreference;
241     @VisibleForTesting
242     LayoutPreference mResetInternetPreference;
243     @VisibleForTesting
244     ConnectedEthernetNetworkController mConnectedEthernetNetworkController;
245     @VisibleForTesting
246     FooterPreference mWifiStatusMessagePreference;
247 
248     /**
249      * Mobile networks list for provider model
250      */
251     private static final String PREF_KEY_PROVIDER_MOBILE_NETWORK = "provider_model_mobile_network";
252     private NetworkMobileProviderController mNetworkMobileProviderController;
253 
254     /**
255      * Tracks whether the user initiated a connection via clicking in order to autoscroll to the
256      * network once connected.
257      */
258     private boolean mClickedConnect;
259 
NetworkProviderSettings()260     public NetworkProviderSettings() {
261         super(DISALLOW_CONFIG_WIFI);
262     }
263 
264     @Override
onViewCreated(View view, Bundle savedInstanceState)265     public void onViewCreated(View view, Bundle savedInstanceState) {
266         super.onViewCreated(view, savedInstanceState);
267         Activity activity = getActivity();
268         if (activity == null) {
269             return;
270         }
271 
272         setPinnedHeaderView(R.layout.progress_header);
273         setProgressBarVisible(false);
274 
275         mWifiManager = activity.getSystemService(WifiManager.class);
276         if (mWifiManager != null) {
277             setLoading(true, false);
278             mIsViewLoading = true;
279         }
280     }
281 
282     @Override
onCreate(Bundle icicle)283     public void onCreate(Bundle icicle) {
284         super.onCreate(icicle);
285         mAirplaneModeEnabler = new AirplaneModeEnabler(getContext(), this);
286 
287         // TODO(b/37429702): Add animations and preference comparator back after initial screen is
288         // loaded (ODR).
289         setAnimationAllowed(false);
290 
291         addPreferences();
292 
293         mIsRestricted = isUiRestricted();
294         mIsAdmin = isAdminUser();
295     }
296 
isAdminUser()297     private boolean isAdminUser() {
298         final UserManager userManager = getSystemService(UserManager.class);
299         if (userManager == null) return true;
300         return userManager.isAdminUser();
301     }
302 
addPreferences()303     private void addPreferences() {
304         addPreferencesFromResource(R.xml.network_provider_settings);
305 
306         mAirplaneModeMsgPreference = findPreference(PREF_KEY_AIRPLANE_MODE_MSG);
307         updateAirplaneModeMsgPreference(mAirplaneModeEnabler.isAirplaneModeOn() /* visible */);
308         mConnectedWifiEntryPreferenceCategory = findPreference(PREF_KEY_CONNECTED_ACCESS_POINTS);
309         mFirstWifiEntryPreferenceCategory = findPreference(PREF_KEY_FIRST_ACCESS_POINTS);
310         mWifiEntryPreferenceCategory = findPreference(PREF_KEY_ACCESS_POINTS);
311         mConfigureWifiSettingsPreference = findPreference(PREF_KEY_CONFIGURE_NETWORK_SETTINGS);
312         mSavedNetworksPreference = findPreference(PREF_KEY_SAVED_NETWORKS);
313         mAddWifiNetworkPreference = new AddWifiNetworkPreference(getPrefContext());
314         mDataUsagePreference = findPreference(PREF_KEY_DATA_USAGE);
315         mDataUsagePreference.setVisible(DataUsageUtils.hasWifiRadio(getContext()));
316         mDataUsagePreference.setTemplate(
317                 NetworkTemplate.buildTemplateWifi(NetworkTemplate.WIFI_NETWORKID_ALL,
318                 null /* subscriberId */), 0 /*subId*/, null /*service*/);
319         mResetInternetPreference = findPreference(PREF_KEY_RESET_INTERNET);
320         if (mResetInternetPreference != null) {
321             mResetInternetPreference.setVisible(false);
322         }
323         addNetworkMobileProviderController();
324         addConnectedEthernetNetworkController();
325         addWifiSwitchPreferenceController();
326         mWifiStatusMessagePreference = findPreference(PREF_KEY_WIFI_STATUS_MESSAGE);
327     }
328 
updateAirplaneModeMsgPreference(boolean visible)329     private void updateAirplaneModeMsgPreference(boolean visible) {
330         if (mAirplaneModeMsgPreference != null) {
331             mAirplaneModeMsgPreference.setVisible(visible);
332         }
333     }
334 
addNetworkMobileProviderController()335     private void addNetworkMobileProviderController() {
336         if (mNetworkMobileProviderController == null) {
337             mNetworkMobileProviderController = new NetworkMobileProviderController(
338                     getContext(), PREF_KEY_PROVIDER_MOBILE_NETWORK);
339         }
340         mNetworkMobileProviderController.init(getSettingsLifecycle());
341         mNetworkMobileProviderController.displayPreference(getPreferenceScreen());
342     }
343 
addConnectedEthernetNetworkController()344     private void addConnectedEthernetNetworkController() {
345         if (mConnectedEthernetNetworkController == null) {
346             mConnectedEthernetNetworkController =
347                     new ConnectedEthernetNetworkController(getContext(), getSettingsLifecycle());
348         }
349         mConnectedEthernetNetworkController.displayPreference(getPreferenceScreen());
350     }
351 
addWifiSwitchPreferenceController()352     private void addWifiSwitchPreferenceController() {
353         if (mWifiSwitchPreferenceController == null) {
354             mWifiSwitchPreferenceController =
355                     new WifiSwitchPreferenceController(getContext(), getSettingsLifecycle());
356         }
357         mWifiSwitchPreferenceController.displayPreference(getPreferenceScreen());
358     }
359 
360     @Override
onActivityCreated(Bundle savedInstanceState)361     public void onActivityCreated(Bundle savedInstanceState) {
362         super.onActivityCreated(savedInstanceState);
363 
364         mWifiPickerTrackerHelper =
365                 new WifiPickerTrackerHelper(getSettingsLifecycle(), getContext(), this);
366         mWifiPickerTracker = mWifiPickerTrackerHelper.getWifiPickerTracker();
367         mInternetUpdater = new InternetUpdater(getContext(), getSettingsLifecycle(), this);
368 
369         mConnectListener = new WifiConnectListener(getActivity());
370 
371         mSaveListener = new WifiManager.ActionListener() {
372             @Override
373             public void onSuccess() {
374             }
375 
376             @Override
377             public void onFailure(int reason) {
378                 Activity activity = getActivity();
379                 if (activity != null) {
380                     Toast.makeText(activity,
381                             R.string.wifi_failed_save_message,
382                             Toast.LENGTH_SHORT).show();
383                 }
384             }
385         };
386 
387         mForgetListener = new WifiManager.ActionListener() {
388             @Override
389             public void onSuccess() {
390             }
391 
392             @Override
393             public void onFailure(int reason) {
394                 Activity activity = getActivity();
395                 if (activity != null) {
396                     Toast.makeText(activity,
397                             R.string.wifi_failed_forget_message,
398                             Toast.LENGTH_SHORT).show();
399                 }
400             }
401         };
402         setHasOptionsMenu(true);
403 
404         if (savedInstanceState != null) {
405             mDialogMode = savedInstanceState.getInt(SAVE_DIALOG_MODE);
406             mDialogWifiEntryKey = savedInstanceState.getString(SAVE_DIALOG_WIFIENTRY_KEY);
407         }
408 
409         // If we're supposed to enable/disable the Next button based on our current connection
410         // state, start it off in the right state.
411         final Intent intent = getActivity().getIntent();
412         mEnableNextOnConnection = intent.getBooleanExtra(EXTRA_ENABLE_NEXT_ON_CONNECT, false);
413 
414         if (intent.hasExtra(EXTRA_START_CONNECT_SSID)) {
415             mOpenSsid = intent.getStringExtra(EXTRA_START_CONNECT_SSID);
416         }
417 
418         if (mNetworkMobileProviderController != null) {
419             mNetworkMobileProviderController.setWifiPickerTrackerHelper(mWifiPickerTrackerHelper);
420         }
421     }
422 
423     @Override
onAttach(Context context)424     public void onAttach(Context context) {
425         super.onAttach(context);
426 
427     }
428 
429     @Override
onStart()430     public void onStart() {
431         super.onStart();
432         if (mIsViewLoading) {
433             final long delayMillis = mWifiManager.isWifiEnabled() ? 1000 : 100;
434             getView().postDelayed(mRemoveLoadingRunnable, delayMillis);
435         }
436         if (mIsRestricted) {
437             restrictUi();
438             return;
439         }
440         mAirplaneModeEnabler.start();
441     }
442 
restrictUi()443     private void restrictUi() {
444         if (!isUiRestrictedByOnlyAdmin()) {
445             getEmptyTextView().setText(R.string.wifi_empty_list_user_restricted);
446         }
447         getPreferenceScreen().removeAll();
448     }
449 
450     @Override
onResume()451     public void onResume() {
452         super.onResume();
453 
454         // Disable the animation of the preference list
455         final RecyclerView prefListView = getListView();
456         if (prefListView != null) {
457             prefListView.setItemAnimator(null);
458         }
459 
460         // Because RestrictedSettingsFragment's onResume potentially requests authorization,
461         // which changes the restriction state, recalculate it.
462         final boolean alreadyImmutablyRestricted = mIsRestricted;
463         mIsRestricted = isUiRestricted();
464         if (!alreadyImmutablyRestricted && mIsRestricted) {
465             restrictUi();
466         }
467 
468         changeNextButtonState(mWifiPickerTracker.getConnectedWifiEntry() != null);
469     }
470 
471     @Override
onStop()472     public void onStop() {
473         mIsWifiEntryListStale = true;
474         getView().removeCallbacks(mRemoveLoadingRunnable);
475         getView().removeCallbacks(mUpdateWifiEntryPreferencesRunnable);
476         getView().removeCallbacks(mHideProgressBarRunnable);
477         mAirplaneModeEnabler.stop();
478         super.onStop();
479     }
480 
481     @Override
onDestroy()482     public void onDestroy() {
483         if (mAirplaneModeEnabler != null) {
484             mAirplaneModeEnabler.close();
485         }
486         super.onDestroy();
487     }
488 
489     @Override
onActivityResult(int requestCode, int resultCode, Intent data)490     public void onActivityResult(int requestCode, int resultCode, Intent data) {
491         super.onActivityResult(requestCode, resultCode, data);
492 
493         if (requestCode == ADD_NETWORK_REQUEST) {
494             handleAddNetworkRequest(resultCode, data);
495             return;
496         } else if (requestCode == REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER) {
497             if (resultCode == Activity.RESULT_OK) {
498                 if (mDialog != null) {
499                     mDialog.dismiss();
500                 }
501             }
502             return;
503         } else if (requestCode == CONFIG_NETWORK_REQUEST) {
504             if (resultCode == Activity.RESULT_OK) {
505                 final WifiConfiguration wifiConfiguration = data.getParcelableExtra(
506                         ConfigureWifiEntryFragment.NETWORK_CONFIG_KEY);
507                 if (wifiConfiguration != null) {
508                     mWifiManager.connect(wifiConfiguration,
509                             new WifiConnectActionListener());
510                 }
511             }
512             return;
513         } else if (requestCode == MANAGE_SUBSCRIPTION) {
514             //Do nothing
515             return;
516         }
517 
518         final boolean formerlyRestricted = mIsRestricted;
519         mIsRestricted = isUiRestricted();
520         if (formerlyRestricted && !mIsRestricted
521                 && getPreferenceScreen().getPreferenceCount() == 0) {
522             // De-restrict the ui
523             addPreferences();
524         }
525     }
526 
527     @Override
onCreateAdapter(PreferenceScreen preferenceScreen)528     protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) {
529         final RecyclerView.Adapter adapter = super.onCreateAdapter(preferenceScreen);
530         adapter.setHasStableIds(true);
531         return adapter;
532     }
533 
534     @Override
getMetricsCategory()535     public int getMetricsCategory() {
536         return SettingsEnums.WIFI;
537     }
538 
539     @Override
onSaveInstanceState(Bundle outState)540     public void onSaveInstanceState(Bundle outState) {
541         super.onSaveInstanceState(outState);
542         // If dialog has been shown, save its state.
543         if (mDialog != null) {
544             outState.putInt(SAVE_DIALOG_MODE, mDialogMode);
545             outState.putString(SAVE_DIALOG_WIFIENTRY_KEY, mDialogWifiEntryKey);
546         }
547     }
548 
549     @Override
onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info)550     public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) {
551         Preference preference = (Preference) view.getTag();
552         if (!(preference instanceof LongPressWifiEntryPreference)) {
553             // Do nothing.
554             return;
555         }
556 
557         // Cache the WifiEntry for onContextItemSelected. Don't use it in other methods.
558         mSelectedWifiEntry = ((LongPressWifiEntryPreference) preference).getWifiEntry();
559 
560         menu.setHeaderTitle(mSelectedWifiEntry.getTitle());
561         if (mSelectedWifiEntry.canConnect()) {
562             menu.add(Menu.NONE, MENU_ID_CONNECT, 0 /* order */, R.string.wifi_connect);
563         }
564 
565         if (mSelectedWifiEntry.canDisconnect()) {
566             if (mSelectedWifiEntry.canShare()) {
567                 addShareMenuIfSuitable(menu);
568             }
569             menu.add(Menu.NONE, MENU_ID_DISCONNECT, 1 /* order */,
570                     R.string.wifi_disconnect_button_text);
571         }
572 
573         // "forget" for normal saved network. And "disconnect" for ephemeral network because it
574         // could only be disconnected and be put in blocklists so it won't be used again.
575         if (canForgetNetwork()) {
576             addForgetMenuIfSuitable(menu);
577         }
578 
579         WifiConfiguration config = mSelectedWifiEntry.getWifiConfiguration();
580         // Some configs are ineditable
581         if (WifiUtils.isNetworkLockedDown(getActivity(), config)) {
582             return;
583         }
584 
585         if (mSelectedWifiEntry.isSaved() && mSelectedWifiEntry.getConnectedState()
586                 != WifiEntry.CONNECTED_STATE_CONNECTED) {
587             menu.add(Menu.NONE, MENU_ID_MODIFY, 0 /* order */, R.string.wifi_modify);
588         }
589     }
590 
591     @VisibleForTesting
addShareMenuIfSuitable(ContextMenu menu)592     void addShareMenuIfSuitable(ContextMenu menu) {
593         if (mIsAdmin) {
594             menu.add(Menu.NONE, MENU_ID_SHARE, 0 /* order */, R.string.share);
595             return;
596         }
597         Log.w(TAG, "Don't add the Wi-Fi share menu because the user is not an admin.");
598         EventLog.writeEvent(0x534e4554, "206986392", -1 /* UID */, "User is not an admin");
599     }
600 
601     @VisibleForTesting
addForgetMenuIfSuitable(ContextMenu menu)602     void addForgetMenuIfSuitable(ContextMenu menu) {
603         if (mIsAdmin) {
604             menu.add(Menu.NONE, MENU_ID_FORGET, 0 /* order */, R.string.forget);
605         }
606     }
607 
canForgetNetwork()608     private boolean canForgetNetwork() {
609         return mSelectedWifiEntry.canForget() && !WifiUtils.isNetworkLockedDown(getActivity(),
610                 mSelectedWifiEntry.getWifiConfiguration());
611     }
612 
613     @Override
onContextItemSelected(MenuItem item)614     public boolean onContextItemSelected(MenuItem item) {
615         switch (item.getItemId()) {
616             case MENU_ID_CONNECT:
617                 connect(mSelectedWifiEntry, true /* editIfNoConfig */, false /* fullScreenEdit */);
618                 return true;
619             case MENU_ID_DISCONNECT:
620                 mSelectedWifiEntry.disconnect(null /* callback */);
621                 return true;
622             case MENU_ID_FORGET:
623                 forget(mSelectedWifiEntry);
624                 return true;
625             case MENU_ID_SHARE:
626                 WifiDppUtils.showLockScreen(getContext(),
627                         () -> launchWifiDppConfiguratorActivity(mSelectedWifiEntry));
628                 return true;
629             case MENU_ID_MODIFY:
630                 showDialog(mSelectedWifiEntry, WifiConfigUiBase2.MODE_MODIFY);
631                 return true;
632             default:
633                 return super.onContextItemSelected(item);
634         }
635     }
636 
637     @Override
onPreferenceTreeClick(Preference preference)638     public boolean onPreferenceTreeClick(Preference preference) {
639         // If the preference has a fragment set, open that
640         if (preference.getFragment() != null) {
641             preference.setOnPreferenceClickListener(null);
642             return super.onPreferenceTreeClick(preference);
643         }
644 
645         if (preference instanceof LongPressWifiEntryPreference) {
646             final WifiEntry selectedEntry =
647                     ((LongPressWifiEntryPreference) preference).getWifiEntry();
648 
649             if (selectedEntry.shouldEditBeforeConnect()) {
650                 launchConfigNewNetworkFragment(selectedEntry);
651                 return true;
652             }
653 
654             connect(selectedEntry, true /* editIfNoConfig */, true /* fullScreenEdit */);
655         } else if (preference == mAddWifiNetworkPreference) {
656             onAddNetworkPressed();
657         } else {
658             return super.onPreferenceTreeClick(preference);
659         }
660         return true;
661     }
662 
launchWifiDppConfiguratorActivity(WifiEntry wifiEntry)663     private void launchWifiDppConfiguratorActivity(WifiEntry wifiEntry) {
664         final Intent intent = WifiDppUtils.getConfiguratorQrCodeGeneratorIntentOrNull(getContext(),
665                 mWifiManager, wifiEntry);
666 
667         if (intent == null) {
668             Log.e(TAG, "Launch Wi-Fi DPP QR code generator with a wrong Wi-Fi network!");
669         } else {
670             mMetricsFeatureProvider.action(SettingsEnums.PAGE_UNKNOWN,
671                     SettingsEnums.ACTION_SETTINGS_SHARE_WIFI_QR_CODE,
672                     SettingsEnums.SETTINGS_WIFI_DPP_CONFIGURATOR,
673                     /* key */ null,
674                     /* value */ Integer.MIN_VALUE);
675 
676             startActivity(intent);
677         }
678     }
679 
showDialog(WifiEntry wifiEntry, int dialogMode)680     private void showDialog(WifiEntry wifiEntry, int dialogMode) {
681         if (WifiUtils.isNetworkLockedDown(getActivity(), wifiEntry.getWifiConfiguration())
682                 && wifiEntry.getConnectedState() == WifiEntry.CONNECTED_STATE_CONNECTED) {
683             RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getActivity(),
684                     RestrictedLockUtilsInternal.getDeviceOwner(getActivity()));
685             return;
686         }
687 
688         if (mDialog != null) {
689             removeDialog(WIFI_DIALOG_ID);
690             mDialog = null;
691         }
692 
693         // Save the access point and edit mode
694         mDialogWifiEntry = wifiEntry;
695         mDialogWifiEntryKey = wifiEntry.getKey();
696         mDialogMode = dialogMode;
697 
698         showDialog(WIFI_DIALOG_ID);
699     }
700 
701     @Override
onCreateDialog(int dialogId)702     public Dialog onCreateDialog(int dialogId) {
703         switch (dialogId) {
704             case WIFI_DIALOG_ID:
705                 // modify network
706                 mDialog = WifiDialog2
707                         .createModal(getActivity(), this, mDialogWifiEntry, mDialogMode);
708                 return mDialog;
709             default:
710                 return super.onCreateDialog(dialogId);
711         }
712     }
713 
714     @Override
onDialogShowing()715     public void onDialogShowing() {
716         super.onDialogShowing();
717         setOnDismissListener(this);
718     }
719 
720     @Override
onDismiss(DialogInterface dialog)721     public void onDismiss(DialogInterface dialog) {
722         // We don't keep any dialog object when dialog was dismissed.
723         mDialog = null;
724         mDialogWifiEntry = null;
725         mDialogWifiEntryKey = null;
726     }
727 
728     @Override
getDialogMetricsCategory(int dialogId)729     public int getDialogMetricsCategory(int dialogId) {
730         switch (dialogId) {
731             case WIFI_DIALOG_ID:
732                 return SettingsEnums.DIALOG_WIFI_AP_EDIT;
733             default:
734                 return 0;
735         }
736     }
737 
738     @Override
onInternetTypeChanged(@nternetUpdater.InternetType int internetType)739     public void onInternetTypeChanged(@InternetUpdater.InternetType int internetType) {
740         ThreadUtils.postOnMainThread(() -> {
741             onWifiStateChanged();
742         });
743     }
744 
745     /** Called when the state of Wifi has changed. */
746     @Override
onWifiStateChanged()747     public void onWifiStateChanged() {
748         if (mIsRestricted) {
749             return;
750         }
751         final int wifiState = mWifiPickerTracker.getWifiState();
752 
753         if (isVerboseLoggingEnabled()) {
754             Log.i(TAG, "onWifiStateChanged called with wifi state: " + wifiState);
755         }
756 
757         if (isFinishingOrDestroyed()) {
758             Log.w(TAG, "onWifiStateChanged shouldn't run when fragment is finishing or destroyed");
759             return;
760         }
761 
762         switch (wifiState) {
763             case WifiManager.WIFI_STATE_ENABLED:
764                 setWifiScanMessage(/* isWifiEnabled */ true);
765                 updateWifiEntryPreferences();
766                 break;
767 
768             case WifiManager.WIFI_STATE_ENABLING:
769                 removeConnectedWifiEntryPreference();
770                 removeWifiEntryPreference();
771                 setProgressBarVisible(true);
772                 break;
773 
774             case WifiManager.WIFI_STATE_DISABLING:
775                 removeConnectedWifiEntryPreference();
776                 removeWifiEntryPreference();
777                 break;
778 
779             case WifiManager.WIFI_STATE_DISABLED:
780                 setWifiScanMessage(/* isWifiEnabled */ false);
781                 removeConnectedWifiEntryPreference();
782                 removeWifiEntryPreference();
783                 setAdditionalSettingsSummaries();
784                 setProgressBarVisible(false);
785                 mClickedConnect = false;
786                 break;
787         }
788     }
789 
790     @VisibleForTesting
setWifiScanMessage(boolean isWifiEnabled)791     void setWifiScanMessage(boolean isWifiEnabled) {
792         final Context context = getContext();
793         if (context == null) {
794             return;
795         }
796 
797         final LocationManager locationManager = context.getSystemService(LocationManager.class);
798         if (isWifiEnabled || !locationManager.isLocationEnabled()
799                 || !mWifiManager.isScanAlwaysAvailable()) {
800             mWifiStatusMessagePreference.setVisible(false);
801             return;
802         }
803         if (TextUtils.isEmpty(mWifiStatusMessagePreference.getTitle())) {
804             AnnotationSpan.LinkInfo info = new AnnotationSpan.LinkInfo(
805                 AnnotationSpan.LinkInfo.DEFAULT_ANNOTATION,
806                 v -> launchWifiScanningFragment());
807             CharSequence text = AnnotationSpan.linkify(
808                 context.getText(R.string.wifi_scan_notify_message), info);
809             mWifiStatusMessagePreference.setTitle(text);
810         }
811         mWifiStatusMessagePreference.setVisible(true);
812     }
813 
launchWifiScanningFragment()814     private void launchWifiScanningFragment() {
815         new SubSettingLauncher(getContext())
816             .setDestination(WifiScanningFragment.class.getName())
817             .setSourceMetricsCategory(SettingsEnums.SETTINGS_NETWORK_CATEGORY)
818             .launch();
819     }
820 
821     @Override
onWifiEntriesChanged()822     public void onWifiEntriesChanged() {
823         if (mIsWifiEntryListStale) {
824             mIsWifiEntryListStale = false;
825             updateWifiEntryPreferences();
826         } else {
827             updateWifiEntryPreferencesDelayed();
828         }
829         changeNextButtonState(mWifiPickerTracker.getConnectedWifiEntry() != null);
830 
831         // Edit the Wi-Fi network of specified SSID.
832         if (mOpenSsid != null) {
833             Optional<WifiEntry> matchedWifiEntry = mWifiPickerTracker.getWifiEntries().stream()
834                     .filter(wifiEntry -> TextUtils.equals(mOpenSsid, wifiEntry.getSsid()))
835                     .filter(wifiEntry -> wifiEntry.getSecurity() != WifiEntry.SECURITY_NONE
836                             && wifiEntry.getSecurity() != WifiEntry.SECURITY_OWE)
837                     .filter(wifiEntry -> !wifiEntry.isSaved()
838                             || isDisabledByWrongPassword(wifiEntry))
839                     .findFirst();
840             if (matchedWifiEntry.isPresent()) {
841                 mOpenSsid = null;
842                 launchConfigNewNetworkFragment(matchedWifiEntry.get());
843             }
844         }
845     }
846 
847     @Override
onNumSavedNetworksChanged()848     public void onNumSavedNetworksChanged() {
849         if (isFinishingOrDestroyed()) {
850             return;
851         }
852         setAdditionalSettingsSummaries();
853     }
854 
855     @Override
onNumSavedSubscriptionsChanged()856     public void onNumSavedSubscriptionsChanged() {
857         if (isFinishingOrDestroyed()) {
858             return;
859         }
860         setAdditionalSettingsSummaries();
861     }
862 
863     /**
864      * Updates WifiEntries from {@link WifiPickerTracker#getWifiEntries()}. Adds a delay to have
865      * progress bar displayed before starting to modify entries.
866      */
updateWifiEntryPreferencesDelayed()867     private void updateWifiEntryPreferencesDelayed() {
868         // Safeguard from some delayed event handling
869         if (getActivity() != null && !mIsRestricted
870                 && mWifiPickerTracker.getWifiState() == WifiManager.WIFI_STATE_ENABLED) {
871             final View view = getView();
872             final Handler handler = view.getHandler();
873             if (handler != null && handler.hasCallbacks(mUpdateWifiEntryPreferencesRunnable)) {
874                 return;
875             }
876             setProgressBarVisible(true);
877             view.postDelayed(mUpdateWifiEntryPreferencesRunnable, 300);
878         }
879     }
880 
updateWifiEntryPreferences()881     protected void updateWifiEntryPreferences() {
882         // bypass the update if the activity and the view are not ready, or it's restricted UI.
883         if (getActivity() == null || getView() == null || mIsRestricted) {
884             return;
885         }
886         // in case state has changed
887         if (mWifiPickerTracker.getWifiState() != WifiManager.WIFI_STATE_ENABLED) {
888             return;
889         }
890 
891         boolean hasAvailableWifiEntries = false;
892         mWifiEntryPreferenceCategory.setVisible(true);
893 
894         final WifiEntry connectedEntry = mWifiPickerTracker.getConnectedWifiEntry();
895         PreferenceCategory connectedWifiPreferenceCategory = getConnectedWifiPreferenceCategory();
896         connectedWifiPreferenceCategory.setVisible(connectedEntry != null);
897         if (connectedEntry != null) {
898             final LongPressWifiEntryPreference connectedPref =
899                     connectedWifiPreferenceCategory.findPreference(connectedEntry.getKey());
900             if (connectedPref == null || connectedPref.getWifiEntry() != connectedEntry) {
901                 connectedWifiPreferenceCategory.removeAll();
902                 final ConnectedWifiEntryPreference pref =
903                         createConnectedWifiEntryPreference(connectedEntry);
904                 pref.setKey(connectedEntry.getKey());
905                 pref.refresh();
906                 connectedWifiPreferenceCategory.addPreference(pref);
907                 pref.setOnPreferenceClickListener(preference -> {
908                     if (connectedEntry.canSignIn()) {
909                         connectedEntry.signIn(null /* callback */);
910                     } else {
911                         launchNetworkDetailsFragment(pref);
912                     }
913                     return true;
914                 });
915                 pref.setOnGearClickListener(preference -> {
916                     launchNetworkDetailsFragment(pref);
917                 });
918 
919                 if (mClickedConnect) {
920                     mClickedConnect = false;
921                     scrollToPreference(connectedWifiPreferenceCategory);
922                 }
923             }
924         } else {
925             connectedWifiPreferenceCategory.removeAll();
926         }
927 
928         int index = 0;
929         cacheRemoveAllPrefs(mWifiEntryPreferenceCategory);
930         List<WifiEntry> wifiEntries = mWifiPickerTracker.getWifiEntries();
931         for (WifiEntry wifiEntry : wifiEntries) {
932             hasAvailableWifiEntries = true;
933 
934             String key = wifiEntry.getKey();
935             LongPressWifiEntryPreference pref =
936                     (LongPressWifiEntryPreference) getCachedPreference(key);
937             if (pref != null) {
938                 if (pref.getWifiEntry() == wifiEntry) {
939                     pref.setOrder(index++);
940                     continue;
941                 } else {
942                     // Create a new preference if the underlying WifiEntry object has changed
943                     removePreference(key);
944                 }
945             }
946 
947             pref = createLongPressWifiEntryPreference(wifiEntry);
948             pref.setKey(wifiEntry.getKey());
949             pref.setOrder(index++);
950             pref.refresh();
951 
952             if (wifiEntry.getHelpUriString() != null) {
953                 pref.setOnButtonClickListener(preference -> {
954                     openSubscriptionHelpPage(wifiEntry);
955                 });
956             }
957             mWifiEntryPreferenceCategory.addPreference(pref);
958         }
959         removeCachedPrefs(mWifiEntryPreferenceCategory);
960 
961         if (!hasAvailableWifiEntries) {
962             setProgressBarVisible(true);
963             Preference pref = new Preference(getPrefContext());
964             pref.setSelectable(false);
965             pref.setSummary(R.string.wifi_empty_list_wifi_on);
966             pref.setOrder(index++);
967             pref.setKey(PREF_KEY_EMPTY_WIFI_LIST);
968             mWifiEntryPreferenceCategory.addPreference(pref);
969         } else {
970             // Continuing showing progress bar for an additional delay to overlap with animation
971             getView().postDelayed(mHideProgressBarRunnable, 1700 /* delay millis */);
972         }
973 
974         mAddWifiNetworkPreference.setOrder(index++);
975         mWifiEntryPreferenceCategory.addPreference(mAddWifiNetworkPreference);
976         setAdditionalSettingsSummaries();
977     }
978 
979     @VisibleForTesting
getConnectedWifiPreferenceCategory()980     PreferenceCategory getConnectedWifiPreferenceCategory() {
981         if (mInternetUpdater.getInternetType() == InternetUpdater.INTERNET_WIFI) {
982             mFirstWifiEntryPreferenceCategory.setVisible(false);
983             mFirstWifiEntryPreferenceCategory.removeAll();
984             return mConnectedWifiEntryPreferenceCategory;
985         }
986 
987         mConnectedWifiEntryPreferenceCategory.setVisible(false);
988         mConnectedWifiEntryPreferenceCategory.removeAll();
989         return mFirstWifiEntryPreferenceCategory;
990     }
991 
992     @VisibleForTesting
createConnectedWifiEntryPreference(WifiEntry wifiEntry)993     ConnectedWifiEntryPreference createConnectedWifiEntryPreference(WifiEntry wifiEntry) {
994         if (mInternetUpdater.getInternetType() == InternetUpdater.INTERNET_WIFI) {
995             return new ConnectedWifiEntryPreference(getPrefContext(), wifiEntry, this);
996         }
997         return new FirstWifiEntryPreference(getPrefContext(), wifiEntry, this);
998     }
999 
launchNetworkDetailsFragment(LongPressWifiEntryPreference pref)1000     private void launchNetworkDetailsFragment(LongPressWifiEntryPreference pref) {
1001         final WifiEntry wifiEntry = pref.getWifiEntry();
1002         final Context context = getContext();
1003         final CharSequence title =
1004                 FeatureFlagUtils.isEnabled(context, FeatureFlags.WIFI_DETAILS_DATAUSAGE_HEADER)
1005                         ? wifiEntry.getTitle()
1006                         : context.getText(R.string.pref_title_network_details);
1007 
1008         final Bundle bundle = new Bundle();
1009         bundle.putString(WifiNetworkDetailsFragment.KEY_CHOSEN_WIFIENTRY_KEY, wifiEntry.getKey());
1010 
1011         new SubSettingLauncher(context)
1012                 .setTitleText(title)
1013                 .setDestination(WifiNetworkDetailsFragment.class.getName())
1014                 .setArguments(bundle)
1015                 .setSourceMetricsCategory(getMetricsCategory())
1016                 .launch();
1017     }
1018 
1019     @VisibleForTesting
createLongPressWifiEntryPreference(WifiEntry wifiEntry)1020     LongPressWifiEntryPreference createLongPressWifiEntryPreference(WifiEntry wifiEntry) {
1021         return new LongPressWifiEntryPreference(getPrefContext(), wifiEntry, this);
1022     }
1023 
launchAddNetworkFragment()1024     private void launchAddNetworkFragment() {
1025         new SubSettingLauncher(getContext())
1026                 .setTitleRes(R.string.wifi_add_network)
1027                 .setDestination(AddNetworkFragment.class.getName())
1028                 .setSourceMetricsCategory(getMetricsCategory())
1029                 .setResultListener(this, ADD_NETWORK_REQUEST)
1030                 .launch();
1031     }
1032 
1033     /** Removes all preferences and hide the {@link #mConnectedWifiEntryPreferenceCategory}. */
removeConnectedWifiEntryPreference()1034     private void removeConnectedWifiEntryPreference() {
1035         mConnectedWifiEntryPreferenceCategory.removeAll();
1036         mConnectedWifiEntryPreferenceCategory.setVisible(false);
1037     }
1038 
removeWifiEntryPreference()1039     private void removeWifiEntryPreference() {
1040         mWifiEntryPreferenceCategory.removeAll();
1041         mWifiEntryPreferenceCategory.setVisible(false);
1042     }
1043 
1044     @VisibleForTesting
setAdditionalSettingsSummaries()1045     void setAdditionalSettingsSummaries() {
1046         mConfigureWifiSettingsPreference.setSummary(getString(
1047                 isWifiWakeupEnabled()
1048                         ? R.string.wifi_configure_settings_preference_summary_wakeup_on
1049                         : R.string.wifi_configure_settings_preference_summary_wakeup_off));
1050 
1051         final int numSavedNetworks = mWifiPickerTracker.getNumSavedNetworks();
1052         final int numSavedSubscriptions = mWifiPickerTracker.getNumSavedSubscriptions();
1053         if (numSavedNetworks + numSavedSubscriptions > 0) {
1054             mSavedNetworksPreference.setVisible(true);
1055             mSavedNetworksPreference.setSummary(
1056                     getSavedNetworkSettingsSummaryText(numSavedNetworks, numSavedSubscriptions));
1057         } else {
1058             mSavedNetworksPreference.setVisible(false);
1059         }
1060     }
1061 
getSavedNetworkSettingsSummaryText( int numSavedNetworks, int numSavedSubscriptions)1062     private String getSavedNetworkSettingsSummaryText(
1063             int numSavedNetworks, int numSavedSubscriptions) {
1064         if (getResources() == null) {
1065             Log.w(TAG, "getSavedNetworkSettingsSummaryText shouldn't run if resource is not ready");
1066             return null;
1067         }
1068 
1069         if (numSavedSubscriptions == 0) {
1070             return getResources().getQuantityString(R.plurals.wifi_saved_access_points_summary,
1071                     numSavedNetworks, numSavedNetworks);
1072         } else if (numSavedNetworks == 0) {
1073             return getResources().getQuantityString(
1074                     R.plurals.wifi_saved_passpoint_access_points_summary,
1075                     numSavedSubscriptions, numSavedSubscriptions);
1076         } else {
1077             final int numTotalEntries = numSavedNetworks + numSavedSubscriptions;
1078             return getResources().getQuantityString(R.plurals.wifi_saved_all_access_points_summary,
1079                     numTotalEntries, numTotalEntries);
1080         }
1081     }
1082 
isWifiWakeupEnabled()1083     private boolean isWifiWakeupEnabled() {
1084         final Context context = getContext();
1085         final PowerManager powerManager = context.getSystemService(PowerManager.class);
1086         final ContentResolver contentResolver = context.getContentResolver();
1087         return mWifiManager.isAutoWakeupEnabled()
1088                 && mWifiManager.isScanAlwaysAvailable()
1089                 && Settings.Global.getInt(contentResolver,
1090                 Settings.Global.AIRPLANE_MODE_ON, 0) == 0
1091                 && !powerManager.isPowerSaveMode();
1092     }
1093 
setProgressBarVisible(boolean visible)1094     protected void setProgressBarVisible(boolean visible) {
1095         showPinnedHeader(visible);
1096     }
1097 
1098     @VisibleForTesting
handleAddNetworkRequest(int result, Intent data)1099     void handleAddNetworkRequest(int result, Intent data) {
1100         if (result == Activity.RESULT_OK) {
1101             handleAddNetworkSubmitEvent(data);
1102         }
1103     }
1104 
handleAddNetworkSubmitEvent(Intent data)1105     private void handleAddNetworkSubmitEvent(Intent data) {
1106         final WifiConfiguration wifiConfiguration = data.getParcelableExtra(
1107                 AddNetworkFragment.WIFI_CONFIG_KEY);
1108         if (wifiConfiguration != null) {
1109             mWifiManager.save(wifiConfiguration, mSaveListener);
1110         }
1111     }
1112 
1113     /**
1114      * Called when "add network" button is pressed.
1115      */
onAddNetworkPressed()1116     private void onAddNetworkPressed() {
1117         launchAddNetworkFragment();
1118     }
1119 
1120     @Override
getHelpResource()1121     public int getHelpResource() {
1122         return R.string.help_url_wifi;
1123     }
1124 
1125     /**
1126      * Renames/replaces "Next" button when appropriate. "Next" button usually exists in
1127      * Wi-Fi setup screens, not in usual wifi settings screen.
1128      *
1129      * @param enabled true when the device is connected to a wifi network.
1130      */
1131     @VisibleForTesting
changeNextButtonState(boolean enabled)1132     void changeNextButtonState(boolean enabled) {
1133         if (mEnableNextOnConnection && hasNextButton()) {
1134             getNextButton().setEnabled(enabled);
1135         }
1136     }
1137 
1138     @Override
onForget(WifiDialog2 dialog)1139     public void onForget(WifiDialog2 dialog) {
1140         forget(dialog.getWifiEntry());
1141     }
1142 
1143     @Override
onSubmit(WifiDialog2 dialog)1144     public void onSubmit(WifiDialog2 dialog) {
1145         final int dialogMode = dialog.getMode();
1146         final WifiConfiguration config = dialog.getController().getConfig();
1147         final WifiEntry wifiEntry = dialog.getWifiEntry();
1148 
1149         if (dialogMode == WifiConfigUiBase2.MODE_MODIFY) {
1150             if (config == null) {
1151                 Toast.makeText(getContext(), R.string.wifi_failed_save_message,
1152                         Toast.LENGTH_SHORT).show();
1153             } else {
1154                 mWifiManager.save(config, mSaveListener);
1155             }
1156         } else if (dialogMode == WifiConfigUiBase2.MODE_CONNECT
1157                 || (dialogMode == WifiConfigUiBase2.MODE_VIEW && wifiEntry.canConnect())) {
1158             if (config == null) {
1159                 connect(wifiEntry, false /* editIfNoConfig */,
1160                         false /* fullScreenEdit*/);
1161             } else {
1162                 mWifiManager.connect(config, new WifiConnectActionListener());
1163             }
1164         }
1165     }
1166 
1167     @Override
onScan(WifiDialog2 dialog, String ssid)1168     public void onScan(WifiDialog2 dialog, String ssid) {
1169         // Launch QR code scanner to join a network.
1170         startActivityForResult(
1171                 WifiDppUtils.getEnrolleeQrCodeScannerIntent(dialog.getContext(), ssid),
1172                 REQUEST_CODE_WIFI_DPP_ENROLLEE_QR_CODE_SCANNER);
1173     }
1174 
forget(WifiEntry wifiEntry)1175     private void forget(WifiEntry wifiEntry) {
1176         mMetricsFeatureProvider.action(getActivity(), SettingsEnums.ACTION_WIFI_FORGET);
1177         wifiEntry.forget(null /* callback */);
1178     }
1179 
1180     @VisibleForTesting
connect(WifiEntry wifiEntry, boolean editIfNoConfig, boolean fullScreenEdit)1181     void connect(WifiEntry wifiEntry, boolean editIfNoConfig, boolean fullScreenEdit) {
1182         mMetricsFeatureProvider.action(getActivity(), SettingsEnums.ACTION_WIFI_CONNECT,
1183                 wifiEntry.isSaved());
1184 
1185         // If it's an unsaved secure WifiEntry, it will callback
1186         // ConnectCallback#onConnectResult with ConnectCallback#CONNECT_STATUS_FAILURE_NO_CONFIG
1187         wifiEntry.connect(new WifiEntryConnectCallback(wifiEntry, editIfNoConfig,
1188                 fullScreenEdit));
1189     }
1190 
1191     private class WifiConnectActionListener implements WifiManager.ActionListener {
1192         @Override
onSuccess()1193         public void onSuccess() {
1194             mClickedConnect = true;
1195         }
1196 
1197         @Override
onFailure(int reason)1198         public void onFailure(int reason) {
1199             if (isFinishingOrDestroyed()) {
1200                 return;
1201             }
1202             Toast.makeText(getContext(), R.string.wifi_failed_connect_message, Toast.LENGTH_SHORT)
1203                     .show();
1204         }
1205     };
1206 
1207     public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
1208             new BaseSearchIndexProvider(R.xml.network_provider_settings) {
1209 
1210                 @Override
1211                 public List<String> getNonIndexableKeys(Context context) {
1212                     final List<String> keys = super.getNonIndexableKeys(context);
1213 
1214                     final WifiManager wifiManager = context.getSystemService(WifiManager.class);
1215                     if (WifiSavedConfigUtils.getAllConfigsCount(context, wifiManager) == 0) {
1216                         keys.add(PREF_KEY_SAVED_NETWORKS);
1217                     }
1218 
1219                     if (!DataUsageUtils.hasWifiRadio(context)) {
1220                         keys.add(PREF_KEY_DATA_USAGE);
1221                     }
1222                     return keys;
1223                 }
1224             };
1225 
1226     private class WifiEntryConnectCallback implements ConnectCallback {
1227         final WifiEntry mConnectWifiEntry;
1228         final boolean mEditIfNoConfig;
1229         final boolean mFullScreenEdit;
1230 
WifiEntryConnectCallback(WifiEntry connectWifiEntry, boolean editIfNoConfig, boolean fullScreenEdit)1231         WifiEntryConnectCallback(WifiEntry connectWifiEntry, boolean editIfNoConfig,
1232                 boolean fullScreenEdit) {
1233             mConnectWifiEntry = connectWifiEntry;
1234             mEditIfNoConfig = editIfNoConfig;
1235             mFullScreenEdit = fullScreenEdit;
1236         }
1237 
1238         @Override
onConnectResult(@onnectStatus int status)1239         public void onConnectResult(@ConnectStatus int status) {
1240             if (isFinishingOrDestroyed()) {
1241                 return;
1242             }
1243 
1244             if (status == ConnectCallback.CONNECT_STATUS_SUCCESS) {
1245                 mClickedConnect = true;
1246             } else if (status == ConnectCallback.CONNECT_STATUS_FAILURE_NO_CONFIG) {
1247                 if (mEditIfNoConfig) {
1248                     // Edit an unsaved secure Wi-Fi network.
1249                     if (mFullScreenEdit) {
1250                         launchConfigNewNetworkFragment(mConnectWifiEntry);
1251                     } else {
1252                         showDialog(mConnectWifiEntry, WifiConfigUiBase2.MODE_CONNECT);
1253                     }
1254                 }
1255             } else if (status == CONNECT_STATUS_FAILURE_UNKNOWN) {
1256                 Toast.makeText(getContext(), R.string.wifi_failed_connect_message,
1257                         Toast.LENGTH_SHORT).show();
1258             }
1259         }
1260     }
1261 
launchConfigNewNetworkFragment(WifiEntry wifiEntry)1262     private void launchConfigNewNetworkFragment(WifiEntry wifiEntry) {
1263         final Bundle bundle = new Bundle();
1264         bundle.putString(WifiNetworkDetailsFragment.KEY_CHOSEN_WIFIENTRY_KEY,
1265                 wifiEntry.getKey());
1266         new SubSettingLauncher(getContext())
1267                 .setTitleText(wifiEntry.getTitle())
1268                 .setDestination(ConfigureWifiEntryFragment.class.getName())
1269                 .setArguments(bundle)
1270                 .setSourceMetricsCategory(getMetricsCategory())
1271                 .setResultListener(NetworkProviderSettings.this, CONFIG_NETWORK_REQUEST)
1272                 .launch();
1273     }
1274 
1275     /** Helper method to return whether a WifiEntry is disabled due to a wrong password */
isDisabledByWrongPassword(WifiEntry wifiEntry)1276     private static boolean isDisabledByWrongPassword(WifiEntry wifiEntry) {
1277         WifiConfiguration config = wifiEntry.getWifiConfiguration();
1278         if (config == null) {
1279             return false;
1280         }
1281         WifiConfiguration.NetworkSelectionStatus networkStatus =
1282                 config.getNetworkSelectionStatus();
1283         if (networkStatus == null
1284                 || networkStatus.getNetworkSelectionStatus() == NETWORK_SELECTION_ENABLED) {
1285             return false;
1286         }
1287         int reason = networkStatus.getNetworkSelectionDisableReason();
1288         return WifiConfiguration.NetworkSelectionStatus.DISABLED_BY_WRONG_PASSWORD == reason;
1289     }
1290 
1291     @VisibleForTesting
openSubscriptionHelpPage(WifiEntry wifiEntry)1292     void openSubscriptionHelpPage(WifiEntry wifiEntry) {
1293         final Intent intent = getHelpIntent(getContext(), wifiEntry.getHelpUriString());
1294         if (intent != null) {
1295             try {
1296                 startActivityForResult(intent, MANAGE_SUBSCRIPTION);
1297             } catch (ActivityNotFoundException e) {
1298                 Log.e(TAG, "Activity was not found for intent, " + intent.toString());
1299             }
1300         }
1301     }
1302 
1303     @VisibleForTesting
getHelpIntent(Context context, String helpUrlString)1304     Intent getHelpIntent(Context context, String helpUrlString) {
1305         return HelpUtils.getHelpIntent(context, helpUrlString, context.getClass().getName());
1306     }
1307 
1308     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)1309     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
1310         if (!mAirplaneModeEnabler.isAirplaneModeOn()) {
1311             MenuItem item = menu.add(0, MENU_FIX_CONNECTIVITY, 0, R.string.fix_connectivity);
1312             item.setIcon(R.drawable.ic_repair_24dp);
1313             item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
1314         }
1315         super.onCreateOptionsMenu(menu, inflater);
1316     }
1317 
1318     @Override
onOptionsItemSelected(MenuItem menuItem)1319     public boolean onOptionsItemSelected(MenuItem menuItem) {
1320         if (menuItem.getItemId() == MENU_FIX_CONNECTIVITY) {
1321             if (isPhoneOnCall()) {
1322                 showResetInternetDialog();
1323                 return true;
1324             }
1325             fixConnectivity();
1326             return true;
1327         }
1328         return super.onOptionsItemSelected(menuItem);
1329     }
1330 
1331     @VisibleForTesting
showResetInternetDialog()1332     void showResetInternetDialog() {
1333         AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
1334         DialogInterface.OnClickListener resetInternetClickListener =
1335                 new Dialog.OnClickListener() {
1336                     @Override
1337                     public void onClick(DialogInterface dialog, int which) {
1338                         fixConnectivity();
1339                     }
1340                 };
1341         builder.setTitle(R.string.reset_your_internet_title)
1342                 .setMessage(R.string.reset_internet_text)
1343                 .setPositiveButton(R.string.tts_reset, resetInternetClickListener)
1344                 .setNegativeButton(android.R.string.cancel, null)
1345                 .create()
1346                 .show();
1347     }
1348 
1349     @VisibleForTesting
isPhoneOnCall()1350     boolean isPhoneOnCall() {
1351         TelephonyManager mTelephonyManager = getActivity().getSystemService(TelephonyManager.class);
1352         int state = mTelephonyManager.getCallState();
1353         return state != TelephonyManager.CALL_STATE_IDLE;
1354     }
1355 
fixConnectivity()1356     private void fixConnectivity() {
1357         if (mInternetResetHelper == null) {
1358             mInternetResetHelper = new InternetResetHelper(getContext(), getLifecycle());
1359             mInternetResetHelper.setResettingPreference(mResetInternetPreference);
1360             mInternetResetHelper.setMobileNetworkController(mNetworkMobileProviderController);
1361             mInternetResetHelper.setWifiTogglePreference(
1362                     findPreference(WifiSwitchPreferenceController.KEY));
1363             mInternetResetHelper.addWifiNetworkPreference(mConnectedWifiEntryPreferenceCategory);
1364             mInternetResetHelper.addWifiNetworkPreference(mFirstWifiEntryPreferenceCategory);
1365             mInternetResetHelper.addWifiNetworkPreference(mWifiEntryPreferenceCategory);
1366         }
1367         mInternetResetHelper.restart();
1368     }
1369 
1370     /**
1371      * Called when airplane mode status is changed.
1372      *
1373      * @param isAirplaneModeOn The airplane mode is on
1374      */
1375     @Override
onAirplaneModeChanged(boolean isAirplaneModeOn)1376     public void onAirplaneModeChanged(boolean isAirplaneModeOn) {
1377         updateAirplaneModeMsgPreference(isAirplaneModeOn /* visible */);
1378     }
1379 
1380     /**
1381      * A Wi-Fi preference for the connected Wi-Fi network without internet access.
1382      *
1383      * Override the icon color attribute by {@link ConnectedWifiEntryPreference#getIconColorAttr()}
1384      * and show the icon color to android.R.attr.colorControlNormal for the preference.
1385      */
1386     public class FirstWifiEntryPreference extends ConnectedWifiEntryPreference {
FirstWifiEntryPreference(Context context, WifiEntry wifiEntry, Fragment fragment)1387         public FirstWifiEntryPreference(Context context, WifiEntry wifiEntry,
1388                 Fragment fragment) {
1389             super(context, wifiEntry, fragment);
1390         }
1391 
1392         @Override
getIconColorAttr()1393         protected int getIconColorAttr() {
1394             return android.R.attr.colorControlNormal;
1395         }
1396     }
1397 }
1398