1 /* 2 * Copyright (C) 2016 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.tv.settings.connectivity; 18 19 import static com.android.tv.settings.overlay.FlavorUtils.FLAVOR_CLASSIC; 20 import static com.android.tv.settings.overlay.FlavorUtils.FLAVOR_TWO_PANEL; 21 import static com.android.tv.settings.overlay.FlavorUtils.FLAVOR_VENDOR; 22 import static com.android.tv.settings.overlay.FlavorUtils.FLAVOR_X; 23 import static com.android.tv.settings.util.InstrumentationUtils.logEntrySelected; 24 import static com.android.tv.settings.util.InstrumentationUtils.logToggleInteracted; 25 26 import android.app.tvsettings.TvSettingsEnums; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.pm.PackageManager; 30 import android.content.pm.ResolveInfo; 31 import android.net.ConnectivityManager; 32 import android.net.NetworkInfo; 33 import android.net.wifi.WifiManager; 34 import android.os.Build; 35 import android.os.Bundle; 36 import android.os.Handler; 37 import android.os.SystemClock; 38 import android.os.SystemProperties; 39 import android.os.UserManager; 40 import android.provider.Settings; 41 import android.util.ArrayMap; 42 import android.view.View; 43 44 import androidx.annotation.Keep; 45 import androidx.preference.Preference; 46 import androidx.preference.PreferenceCategory; 47 import androidx.preference.PreferenceManager; 48 import androidx.preference.TwoStatePreference; 49 50 import com.android.settingslib.RestrictedPreference; 51 import com.android.tv.settings.MainFragment; 52 import com.android.tv.settings.R; 53 import com.android.tv.settings.RestrictedPreferenceAdapter; 54 import com.android.tv.settings.SettingsPreferenceFragment; 55 import com.android.tv.settings.basic.BasicModeFeatureProvider; 56 import com.android.tv.settings.library.network.AccessPoint; 57 import com.android.tv.settings.overlay.FlavorUtils; 58 import com.android.tv.settings.util.SliceUtils; 59 import com.android.tv.settings.widget.AccessPointPreference; 60 import com.android.tv.settings.widget.CustomContentDescriptionSwitchPreference; 61 import com.android.tv.settings.widget.TvAccessPointPreference; 62 import com.android.tv.twopanelsettings.slices.SlicePreference; 63 import com.android.wifitrackerlib.WifiEntry; 64 65 import java.util.ArrayList; 66 import java.util.Collection; 67 import java.util.Collections; 68 import java.util.HashSet; 69 import java.util.List; 70 import java.util.Map; 71 import java.util.Set; 72 73 /** 74 * Fragment for controlling network connectivity 75 */ 76 @Keep 77 public class NetworkFragment extends SettingsPreferenceFragment implements 78 ConnectivityListener.Listener, ConnectivityListener.WifiNetworkListener, 79 AccessPoint.AccessPointListener { 80 81 private static final String KEY_WIFI_ENABLE = "wifi_enable"; 82 private static final String KEY_WIFI_LIST = "wifi_list"; 83 private static final String KEY_WIFI_COLLAPSE = "wifi_collapse"; 84 private static final String KEY_WIFI_OTHER = "wifi_other"; 85 private static final String KEY_WIFI_ADD = "wifi_add"; 86 private static final String KEY_WIFI_ADD_EASYCONNECT = "wifi_add_easyconnect"; 87 private static final String KEY_WIFI_ALWAYS_SCAN = "wifi_always_scan"; 88 private static final String KEY_ETHERNET = "ethernet"; 89 private static final String KEY_ETHERNET_STATUS = "ethernet_status"; 90 private static final String KEY_ETHERNET_PROXY = "ethernet_proxy"; 91 private static final String KEY_ETHERNET_DHCP = "ethernet_dhcp"; 92 private static final String KEY_NETWORK_DIAGNOSTICS = "network_diagnostics"; 93 94 private static final int INITIAL_UPDATE_DELAY = 500; 95 96 private static final String NETWORK_DIAGNOSTICS_ACTION = 97 "com.android.tv.settings.network.NETWORK_DIAGNOSTICS"; 98 99 private ConnectivityListener mConnectivityListener; 100 private WifiManager mWifiManager; 101 private ConnectivityManager mConnectivityManager; 102 private TvAccessPointPreference.UserBadgeCache mUserBadgeCache; 103 104 private TwoStatePreference mEnableWifiPref; 105 private CollapsibleCategory mWifiNetworksCategory; 106 private Preference mCollapsePref; 107 private RestrictedPreference mAddPref; 108 private RestrictedPreference mAddEasyConnectPref; 109 private TwoStatePreference mAlwaysScan; 110 private PreferenceCategory mEthernetCategory; 111 private Preference mEthernetStatusPref; 112 private Preference mEthernetProxyPref; 113 private Preference mEthernetDhcpPref; 114 private Map<WifiEntry, RestrictedPreferenceAdapter<TvAccessPointPreference>> mPrefMap = 115 Collections.emptyMap(); 116 117 private final Handler mHandler = new Handler(); 118 private long mNoWifiUpdateBeforeMillis; 119 private Runnable mInitialUpdateWifiListRunnable = new Runnable() { 120 @Override 121 public void run() { 122 mNoWifiUpdateBeforeMillis = 0; 123 updateWifiList(); 124 } 125 }; 126 private boolean mIsWifiHardwarePresent; 127 newInstance()128 public static NetworkFragment newInstance() { 129 return new NetworkFragment(); 130 } 131 132 private final List<AccessPoint> mCurrentAccessPoints = new ArrayList<>(); 133 134 @Override onCreate(Bundle savedInstanceState)135 public void onCreate(Bundle savedInstanceState) { 136 mIsWifiHardwarePresent = getContext().getPackageManager() 137 .hasSystemFeature(PackageManager.FEATURE_WIFI); 138 mConnectivityListener = new ConnectivityListener( 139 getContext(), this, getSettingsLifecycle()); 140 mWifiManager = getContext().getSystemService(WifiManager.class); 141 mConnectivityManager = getContext().getSystemService(ConnectivityManager.class); 142 mUserBadgeCache = 143 new TvAccessPointPreference.UserBadgeCache(getContext().getPackageManager()); 144 super.onCreate(savedInstanceState); 145 } 146 147 @Override onStart()148 public void onStart() { 149 super.onStart(); 150 mConnectivityListener.setWifiListener(this); 151 mNoWifiUpdateBeforeMillis = SystemClock.elapsedRealtime() + INITIAL_UPDATE_DELAY; 152 updateWifiList(); 153 } 154 155 @Override onResume()156 public void onResume() { 157 super.onResume(); 158 // There doesn't seem to be an API to listen to everything this could cover, so 159 // tickle it here and hope for the best. 160 updateConnectivity(); 161 } 162 163 164 @Override onStop()165 public void onStop() { 166 mConnectivityListener.setListener(null); 167 clearCurrentAccessPoints(); 168 super.onStop(); 169 } 170 getPreferenceScreenResId()171 private int getPreferenceScreenResId() { 172 switch (FlavorUtils.getFlavor(getContext())) { 173 case FLAVOR_CLASSIC: 174 case FLAVOR_TWO_PANEL: 175 return R.xml.network; 176 case FLAVOR_X: 177 case FLAVOR_VENDOR: 178 return R.xml.network_x; 179 default: 180 return R.xml.network; 181 } 182 } 183 184 @Override onCreatePreferences(Bundle savedInstanceState, String rootKey)185 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 186 getPreferenceManager().setPreferenceComparisonCallback( 187 new PreferenceManager.SimplePreferenceComparisonCallback()); 188 setPreferencesFromResource(getPreferenceScreenResId(), null); 189 190 mEnableWifiPref = (TwoStatePreference) findPreference(KEY_WIFI_ENABLE); 191 mWifiNetworksCategory = (CollapsibleCategory) findPreference(KEY_WIFI_LIST); 192 mCollapsePref = findPreference(KEY_WIFI_COLLAPSE); 193 mAddPref = (RestrictedPreference) findPreference(KEY_WIFI_ADD); 194 mAddEasyConnectPref = (RestrictedPreference) findPreference(KEY_WIFI_ADD_EASYCONNECT); 195 mAlwaysScan = (TwoStatePreference) findPreference(KEY_WIFI_ALWAYS_SCAN); 196 197 mEthernetCategory = (PreferenceCategory) findPreference(KEY_ETHERNET); 198 mEthernetStatusPref = findPreference(KEY_ETHERNET_STATUS); 199 mEthernetProxyPref = findPreference(KEY_ETHERNET_PROXY); 200 mEthernetDhcpPref = findPreference(KEY_ETHERNET_DHCP); 201 202 if (!mIsWifiHardwarePresent) { 203 mEnableWifiPref.setVisible(false); 204 } 205 206 Preference networkDiagnosticsPref = findPreference(KEY_NETWORK_DIAGNOSTICS); 207 Intent networkDiagnosticsIntent = makeNetworkDiagnosticsIntent(); 208 if (networkDiagnosticsIntent != null) { 209 networkDiagnosticsPref.setVisible(true); 210 networkDiagnosticsPref.setIntent(networkDiagnosticsIntent); 211 } else { 212 networkDiagnosticsPref.setVisible(false); 213 } 214 215 final UserManager userManager = UserManager.get(getContext()); 216 217 mAddPref.checkRestrictionAndSetDisabled(UserManager.DISALLOW_CONFIG_WIFI); 218 mAddEasyConnectPref.checkRestrictionAndSetDisabled(UserManager.DISALLOW_CONFIG_WIFI); 219 220 if (!mAddPref.isDisabledByAdmin()) { 221 mAddPref.checkRestrictionAndSetDisabled(UserManager.DISALLOW_ADD_WIFI_CONFIG); 222 mAddEasyConnectPref.checkRestrictionAndSetDisabled( 223 UserManager.DISALLOW_ADD_WIFI_CONFIG); 224 } 225 226 if (userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI) 227 || userManager.hasUserRestriction(UserManager.DISALLOW_ADD_WIFI_CONFIG)) { 228 mAddPref.setFragment(null); 229 mAddEasyConnectPref.setFragment(null); 230 231 if (!mAddPref.isDisabledByAdmin()) { 232 mAddPref.setEnabled(false); 233 } 234 if (!mAddEasyConnectPref.isDisabledByAdmin()) { 235 mAddEasyConnectPref.setEnabled(false); 236 } 237 } 238 } 239 240 @Override onPreferenceTreeClick(Preference preference)241 public boolean onPreferenceTreeClick(Preference preference) { 242 if (preference.getKey() == null) { 243 return super.onPreferenceTreeClick(preference); 244 } 245 switch (preference.getKey()) { 246 case KEY_WIFI_ENABLE: 247 BasicModeFeatureProvider provider = FlavorUtils.getFeatureFactory( 248 getContext()).getBasicModeFeatureProvider(); 249 if (mEnableWifiPref.isChecked() && 250 Settings.Global.getInt( 251 getContext().getContentResolver(), Settings.Global.ADB_ENABLED, 0) 252 != 1 && SystemProperties.getInt( 253 "ro.product.first_api_level", Build.VERSION.SDK_INT) 254 >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && provider.isBasicMode( 255 getContext()) 256 && !provider.isStoreDemoMode(getContext())) { 257 // WiFi turned ON + not developer + launched on U+ + basic mode. 258 // Prevent WiFi connection by launching dialog instead. 259 provider.startBasicModeInternetBlock(getActivity()); 260 } else { 261 mConnectivityListener.setWifiEnabled(mEnableWifiPref.isChecked()); 262 } 263 logToggleInteracted( 264 TvSettingsEnums.NETWORK_WIFI_ON_OFF, mEnableWifiPref.isChecked()); 265 return true; 266 case KEY_WIFI_COLLAPSE: 267 final boolean collapse = !mWifiNetworksCategory.isCollapsed(); 268 View collapsePrefView = getListView().getChildAt(mCollapsePref.getOrder()); 269 String wifiCollapseTitle = getContext().getString(collapse 270 ? R.string.wifi_setting_see_all : R.string.wifi_setting_see_fewer); 271 mCollapsePref.setTitle(wifiCollapseTitle); 272 if (collapsePrefView != null) { 273 collapsePrefView.setAccessibilityPaneTitle(wifiCollapseTitle); 274 } 275 mWifiNetworksCategory.setCollapsed(collapse); 276 logEntrySelected( 277 collapse 278 ? TvSettingsEnums.NETWORK_SEE_FEWER 279 : TvSettingsEnums.NETWORK_SEE_ALL); 280 return true; 281 case KEY_WIFI_ALWAYS_SCAN: 282 Settings.Global.putInt(getActivity().getContentResolver(), 283 Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 284 mAlwaysScan.isChecked() ? 1 : 0); 285 logToggleInteracted( 286 TvSettingsEnums.NETWORK_ALWAYS_SCANNING_NETWORKS, mAlwaysScan.isChecked()); 287 return true; 288 case KEY_ETHERNET_STATUS: 289 return true; 290 case KEY_WIFI_ADD: 291 logEntrySelected(TvSettingsEnums.NETWORK_ADD_NEW_NETWORK); 292 break; 293 case KEY_WIFI_ADD_EASYCONNECT: 294 startActivity(AddWifiNetworkActivity.createEasyConnectIntent(getContext())); 295 break; 296 } 297 return super.onPreferenceTreeClick(preference); 298 } 299 updateConnectivity()300 private void updateConnectivity() { 301 if (!isAdded()) { 302 return; 303 } 304 305 final boolean wifiEnabled = mIsWifiHardwarePresent 306 && mConnectivityListener.isWifiEnabledOrEnabling(); 307 final boolean ethernetConnected = mConnectivityListener.isEthernetConnected(); 308 mEnableWifiPref.setChecked(wifiEnabled); 309 310 mWifiNetworksCategory.setVisible(wifiEnabled && !ethernetConnected); 311 mCollapsePref.setVisible(wifiEnabled && mWifiNetworksCategory.shouldShowCollapsePref()); 312 mAddPref.setVisible(wifiEnabled && !ethernetConnected); 313 if (mAddEasyConnectPref != null) { 314 mAddEasyConnectPref.setVisible(isEasyConnectEnabled() && !ethernetConnected); 315 } 316 317 if (!wifiEnabled) { 318 updateWifiList(); 319 } 320 321 int scanAlwaysAvailable = 0; 322 try { 323 scanAlwaysAvailable = Settings.Global.getInt(getContext().getContentResolver(), 324 Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE); 325 } catch (Settings.SettingNotFoundException e) { 326 // Ignore 327 } 328 mAlwaysScan.setChecked(scanAlwaysAvailable == 1); 329 if (mAlwaysScan instanceof CustomContentDescriptionSwitchPreference) { 330 ((CustomContentDescriptionSwitchPreference) mAlwaysScan).setContentDescription( 331 getResources() 332 .getString( 333 R.string.wifi_setting_always_scan_content_description)); 334 } 335 336 final boolean ethernetAvailable = mConnectivityListener.isEthernetAvailable(); 337 mEthernetCategory.setVisible(ethernetAvailable); 338 mEthernetStatusPref.setVisible(ethernetAvailable); 339 mEthernetProxyPref.setVisible(ethernetAvailable); 340 if (ethernetAvailable) { 341 mEthernetProxyPref.setIntent(EditProxySettingsActivity.createEthernetIntent( 342 getContext(), 343 mConnectivityListener.getEthernetInterfaceName(), 344 mConnectivityListener.getEthernetIpConfiguration())); 345 } 346 mEthernetProxyPref.setOnPreferenceClickListener( 347 preference -> { 348 logEntrySelected(TvSettingsEnums.NETWORK_ETHERNET_PROXY_SETTINGS); 349 return false; 350 }); 351 352 mEthernetDhcpPref.setVisible(ethernetAvailable); 353 if (ethernetAvailable) { 354 mEthernetDhcpPref.setIntent(EditIpSettingsActivity.createEthernetIntent(getContext(), 355 mConnectivityListener.getEthernetInterfaceName(), 356 mConnectivityListener.getEthernetIpConfiguration())); 357 } 358 mEthernetDhcpPref.setOnPreferenceClickListener( 359 preference -> { 360 logEntrySelected(TvSettingsEnums.NETWORK_ETHERNET_IP_SETTINGS); 361 return false; 362 }); 363 364 if (ethernetAvailable) { 365 mEthernetStatusPref.setTitle(ethernetConnected 366 ? R.string.connected : R.string.not_connected); 367 mEthernetStatusPref.setSummary(mConnectivityListener.getEthernetIpAddress()); 368 } 369 370 mEnableWifiPref.setSummary(ethernetConnected ? 371 getString(R.string.unplug_ethernet_to_use_wifi) : null); 372 } 373 updateWifiList()374 private void updateWifiList() { 375 if (!isAdded()) { 376 return; 377 } 378 379 if (!mIsWifiHardwarePresent || !mConnectivityListener.isWifiEnabledOrEnabling() 380 || mConnectivityListener.isEthernetConnected()) { 381 mWifiNetworksCategory.removeAll(); 382 mNoWifiUpdateBeforeMillis = 0; 383 return; 384 } 385 386 final long now = SystemClock.elapsedRealtime(); 387 if (mNoWifiUpdateBeforeMillis > now) { 388 mHandler.removeCallbacks(mInitialUpdateWifiListRunnable); 389 mHandler.postDelayed(mInitialUpdateWifiListRunnable, 390 mNoWifiUpdateBeforeMillis - now); 391 return; 392 } 393 394 final int existingCount = mWifiNetworksCategory.getRealPreferenceCount(); 395 final Set<Preference> toRemove = new HashSet<>(existingCount); 396 for (int i = 0; i < existingCount; i++) { 397 toRemove.add(mWifiNetworksCategory.getPreference(i)); 398 } 399 400 final Context themedContext = getPreferenceManager().getContext(); 401 final Collection<AccessPoint> newAccessPoints = 402 mConnectivityListener.getAvailableNetworks(); 403 int index = 0; 404 405 final Map<WifiEntry, RestrictedPreferenceAdapter<TvAccessPointPreference>> newPrefMap = 406 new ArrayMap<>(); 407 for (final AccessPoint accessPoint : newAccessPoints) { 408 accessPoint.setListener(this); 409 RestrictedPreferenceAdapter<TvAccessPointPreference> restrictedPref = 410 mPrefMap.get(accessPoint.getWifiEntry()); 411 Preference pref; 412 if (restrictedPref == null) { 413 pref = new TvAccessPointPreference(accessPoint, themedContext, mUserBadgeCache, 414 false); 415 List<String> userRestrictions = new ArrayList<>(); 416 userRestrictions.add(UserManager.DISALLOW_CONFIG_WIFI); 417 userRestrictions.add(UserManager.DISALLOW_ADD_WIFI_CONFIG); 418 restrictedPref = new RestrictedPreferenceAdapter(themedContext, pref, 419 userRestrictions); 420 restrictedPref.setApSaved(accessPoint.isSaved()); 421 } else { 422 restrictedPref.setApSaved(accessPoint.isSaved()); 423 toRemove.remove(restrictedPref.getPreference()); 424 pref = restrictedPref.getOriginalPreference(); 425 } 426 newPrefMap.put(accessPoint.getWifiEntry(), restrictedPref); 427 428 if (isCaptivePortal(accessPoint)) { 429 pref.setFragment(null); 430 pref.setIntent(null); 431 pref.setOnPreferenceClickListener(preference -> { 432 accessPoint.getWifiEntry().signIn(null); 433 return true; 434 }); 435 } else if (accessPoint.isActive()) { 436 pref.setFragment(WifiDetailsFragment.class.getName()); 437 // No need to track entry selection as new page will be focused 438 pref.setOnPreferenceClickListener(preference -> false); 439 WifiDetailsFragment.prepareArgs(pref.getExtras(), accessPoint); 440 pref.setIntent(null); 441 } else { 442 pref.setFragment(null); 443 pref.setIntent(WifiConnectionActivity.createIntent(getContext(), accessPoint)); 444 pref.setOnPreferenceClickListener( 445 preference -> { 446 logEntrySelected(TvSettingsEnums.NETWORK_NOT_CONNECTED_AP); 447 return false; 448 }); 449 } 450 pref.setVisible(!restrictedPref.isRestricted(UserManager.DISALLOW_CONFIG_WIFI) 451 || accessPoint.isSaved()); 452 pref.setOrder(index++); 453 pref.setSummary(WifiUtils.getConnectionStatus(accessPoint.getWifiEntry())); 454 restrictedPref.updatePreference(); 455 456 Preference restrictedChild = restrictedPref.getPreference(); 457 if (restrictedChild.getParent() != null && 458 restrictedChild.getParent() != mWifiNetworksCategory) { 459 // Remove first if added to parent from old fragment. 460 restrictedChild.getParent().removePreference(restrictedChild); 461 } 462 mWifiNetworksCategory.addPreference(restrictedChild); 463 } 464 465 for (final Preference preference : toRemove) { 466 mWifiNetworksCategory.removePreference(preference); 467 } 468 469 mCollapsePref.setVisible(mWifiNetworksCategory.shouldShowCollapsePref()); 470 mPrefMap = newPrefMap; 471 472 clearCurrentAccessPoints(); 473 mCurrentAccessPoints.addAll(newAccessPoints); 474 } 475 clearCurrentAccessPoints()476 private void clearCurrentAccessPoints() { 477 for (AccessPoint accessPoint : mCurrentAccessPoints) { 478 accessPoint.setListener(null); 479 } 480 mCurrentAccessPoints.clear(); 481 } 482 isCaptivePortal(AccessPoint accessPoint)483 private boolean isCaptivePortal(AccessPoint accessPoint) { 484 return accessPoint.getWifiEntry().canSignIn(); 485 } 486 makeNetworkDiagnosticsIntent()487 private Intent makeNetworkDiagnosticsIntent() { 488 Intent intent = new Intent(); 489 intent.setAction(NETWORK_DIAGNOSTICS_ACTION); 490 491 ResolveInfo resolveInfo = MainFragment.systemIntentIsHandled(getContext(), intent); 492 if (resolveInfo == null || resolveInfo.activityInfo == null) { 493 return null; 494 } 495 496 intent.setPackage(resolveInfo.activityInfo.packageName); 497 498 return intent; 499 } 500 501 @Override onConnectivityChange()502 public void onConnectivityChange() { 503 updateConnectivity(); 504 } 505 506 @Override onWifiListChanged()507 public void onWifiListChanged() { 508 updateWifiList(); 509 } 510 511 @Override onAccessPointChanged(AccessPoint accessPoint)512 public void onAccessPointChanged(AccessPoint accessPoint) { 513 RestrictedPreferenceAdapter<TvAccessPointPreference> restrictedPref = 514 mPrefMap.get(accessPoint.getWifiEntry()); 515 restrictedPref.updatePreference(AccessPointPreference::refresh); 516 } 517 518 @Override onLevelChanged(AccessPoint accessPoint)519 public void onLevelChanged(AccessPoint accessPoint) { 520 RestrictedPreferenceAdapter<TvAccessPointPreference> restrictedPref = 521 mPrefMap.get(accessPoint.getWifiEntry()); 522 restrictedPref.updatePreference(AccessPointPreference::onLevelChanged); 523 } 524 525 @Override getPageId()526 protected int getPageId() { 527 return TvSettingsEnums.NETWORK; 528 } 529 isEasyConnectEnabled()530 private boolean isEasyConnectEnabled() { 531 final boolean wifiEnabled = mIsWifiHardwarePresent 532 && mConnectivityListener.isWifiEnabledOrEnabling(); 533 534 if (!wifiEnabled || !mWifiManager.isEasyConnectSupported()) { 535 return false; 536 } 537 538 return getContext().getResources().getBoolean(R.bool.config_easyconnect_enabled); 539 } 540 } 541