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