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