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