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.util.InstrumentationUtils.logEntrySelected; 20 import static com.android.tv.settings.util.InstrumentationUtils.logToggleInteracted; 21 22 import android.app.tvsettings.TvSettingsEnums; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.pm.PackageManager; 26 import android.net.ConnectivityManager; 27 import android.net.NetworkCapabilities; 28 import android.net.NetworkInfo; 29 import android.net.wifi.WifiConfiguration; 30 import android.net.wifi.WifiManager; 31 import android.os.Bundle; 32 import android.os.Handler; 33 import android.os.SystemClock; 34 import android.provider.Settings; 35 36 import androidx.annotation.Keep; 37 import androidx.preference.Preference; 38 import androidx.preference.PreferenceCategory; 39 import androidx.preference.PreferenceManager; 40 import androidx.preference.TwoStatePreference; 41 42 import com.android.internal.logging.nano.MetricsProto; 43 import com.android.settingslib.wifi.AccessPoint; 44 import com.android.settingslib.wifi.AccessPointPreference; 45 import com.android.tv.settings.R; 46 import com.android.tv.settings.SettingsPreferenceFragment; 47 import com.android.tv.settings.util.SliceUtils; 48 import com.android.tv.twopanelsettings.slices.SlicePreference; 49 50 import java.util.Collection; 51 import java.util.HashSet; 52 import java.util.Set; 53 54 /** 55 * Fragment for controlling network connectivity 56 */ 57 @Keep 58 public class NetworkFragment extends SettingsPreferenceFragment implements 59 ConnectivityListener.Listener, ConnectivityListener.WifiNetworkListener, 60 AccessPoint.AccessPointListener { 61 62 private static final String KEY_WIFI_ENABLE = "wifi_enable"; 63 private static final String KEY_WIFI_LIST = "wifi_list"; 64 private static final String KEY_WIFI_COLLAPSE = "wifi_collapse"; 65 private static final String KEY_WIFI_OTHER = "wifi_other"; 66 private static final String KEY_WIFI_ADD = "wifi_add"; 67 private static final String KEY_WIFI_ALWAYS_SCAN = "wifi_always_scan"; 68 private static final String KEY_ETHERNET = "ethernet"; 69 private static final String KEY_ETHERNET_STATUS = "ethernet_status"; 70 private static final String KEY_ETHERNET_PROXY = "ethernet_proxy"; 71 private static final String KEY_ETHERNET_DHCP = "ethernet_dhcp"; 72 private static final String KEY_DATA_SAVER_SLICE = "data_saver_slice"; 73 private static final String KEY_DATA_ALERT_SLICE = "data_alert_slice"; 74 private static final String ACTION_DATA_ALERT_SETTINGS = "android.settings.DATA_ALERT_SETTINGS"; 75 private static final int INITIAL_UPDATE_DELAY = 500; 76 77 private ConnectivityListener mConnectivityListener; 78 private WifiManager mWifiManager; 79 private ConnectivityManager mConnectivityManager; 80 private AccessPointPreference.UserBadgeCache mUserBadgeCache; 81 82 private TwoStatePreference mEnableWifiPref; 83 private CollapsibleCategory mWifiNetworksCategory; 84 private Preference mCollapsePref; 85 private Preference mAddPref; 86 private TwoStatePreference mAlwaysScan; 87 private PreferenceCategory mEthernetCategory; 88 private Preference mEthernetStatusPref; 89 private Preference mEthernetProxyPref; 90 private Preference mEthernetDhcpPref; 91 private PreferenceCategory mWifiOther; 92 93 private final Handler mHandler = new Handler(); 94 private long mNoWifiUpdateBeforeMillis; 95 private Runnable mInitialUpdateWifiListRunnable = new Runnable() { 96 @Override 97 public void run() { 98 mNoWifiUpdateBeforeMillis = 0; 99 updateWifiList(); 100 } 101 }; 102 private boolean mIsWifiHardwarePresent; 103 newInstance()104 public static NetworkFragment newInstance() { 105 return new NetworkFragment(); 106 } 107 108 @Override onCreate(Bundle savedInstanceState)109 public void onCreate(Bundle savedInstanceState) { 110 mIsWifiHardwarePresent = getContext().getPackageManager() 111 .hasSystemFeature(PackageManager.FEATURE_WIFI); 112 mConnectivityListener = new ConnectivityListener(getContext(), this, getLifecycle()); 113 mWifiManager = getContext().getSystemService(WifiManager.class); 114 mConnectivityManager = getContext().getSystemService(ConnectivityManager.class); 115 mUserBadgeCache = 116 new AccessPointPreference.UserBadgeCache(getContext().getPackageManager()); 117 super.onCreate(savedInstanceState); 118 } 119 120 @Override onStart()121 public void onStart() { 122 super.onStart(); 123 mConnectivityListener.setWifiListener(this); 124 mNoWifiUpdateBeforeMillis = SystemClock.elapsedRealtime() + INITIAL_UPDATE_DELAY; 125 updateWifiList(); 126 } 127 128 @Override onResume()129 public void onResume() { 130 super.onResume(); 131 // There doesn't seem to be an API to listen to everything this could cover, so 132 // tickle it here and hope for the best. 133 updateConnectivity(); 134 } 135 136 @Override onCreatePreferences(Bundle savedInstanceState, String rootKey)137 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 138 getPreferenceManager().setPreferenceComparisonCallback( 139 new PreferenceManager.SimplePreferenceComparisonCallback()); 140 setPreferencesFromResource(R.xml.network, null); 141 142 mEnableWifiPref = (TwoStatePreference) findPreference(KEY_WIFI_ENABLE); 143 mWifiNetworksCategory = (CollapsibleCategory) findPreference(KEY_WIFI_LIST); 144 mCollapsePref = findPreference(KEY_WIFI_COLLAPSE); 145 mAddPref = findPreference(KEY_WIFI_ADD); 146 mAlwaysScan = (TwoStatePreference) findPreference(KEY_WIFI_ALWAYS_SCAN); 147 mWifiOther = (PreferenceCategory) findPreference(KEY_WIFI_OTHER); 148 149 mEthernetCategory = (PreferenceCategory) findPreference(KEY_ETHERNET); 150 mEthernetStatusPref = findPreference(KEY_ETHERNET_STATUS); 151 mEthernetProxyPref = findPreference(KEY_ETHERNET_PROXY); 152 mEthernetProxyPref.setIntent(EditProxySettingsActivity.createIntent(getContext(), 153 WifiConfiguration.INVALID_NETWORK_ID)); 154 mEthernetDhcpPref = findPreference(KEY_ETHERNET_DHCP); 155 mEthernetDhcpPref.setIntent(EditIpSettingsActivity.createIntent(getContext(), 156 WifiConfiguration.INVALID_NETWORK_ID)); 157 158 if (!mIsWifiHardwarePresent) { 159 mEnableWifiPref.setVisible(false); 160 mWifiOther.setVisible(false); 161 } 162 Preference dataSaverSlicePref = findPreference(KEY_DATA_SAVER_SLICE); 163 Preference dataAlertSlicePref = findPreference(KEY_DATA_ALERT_SLICE); 164 boolean isDataSaverVisible = isConnected() && SliceUtils.isSliceProviderValid( 165 getContext(), ((SlicePreference) dataSaverSlicePref).getUri()); 166 boolean isDataAlertVisible = isConnected() && SliceUtils.isSliceProviderValid( 167 getContext(), ((SlicePreference) dataAlertSlicePref).getUri()); 168 169 dataSaverSlicePref.setVisible(isDataSaverVisible); 170 dataAlertSlicePref.setVisible(isDataAlertVisible); 171 Intent i = getActivity().getIntent(); 172 if (i != null && i.getAction() != null) { 173 if (i.getAction().equals(Settings.ACTION_DATA_SAVER_SETTINGS) 174 && dataSaverSlicePref.isVisible()) { 175 mHandler.post(() -> scrollToPreference(dataSaverSlicePref)); 176 } else if (i.getAction().equals(ACTION_DATA_ALERT_SETTINGS) 177 && dataAlertSlicePref.isVisible()) { 178 mHandler.post(() -> scrollToPreference(dataAlertSlicePref)); 179 } 180 } 181 } 182 183 @Override onPreferenceTreeClick(Preference preference)184 public boolean onPreferenceTreeClick(Preference preference) { 185 if (preference.getKey() == null) { 186 return super.onPreferenceTreeClick(preference); 187 } 188 switch (preference.getKey()) { 189 case KEY_WIFI_ENABLE: 190 mConnectivityListener.setWifiEnabled(mEnableWifiPref.isChecked()); 191 logToggleInteracted( 192 TvSettingsEnums.NETWORK_WIFI_ON_OFF, mEnableWifiPref.isChecked()); 193 if (mMetricsFeatureProvider != null) { 194 if (mEnableWifiPref.isChecked()) { 195 mMetricsFeatureProvider.action(getContext(), 196 MetricsProto.MetricsEvent.ACTION_WIFI_ON); 197 } else { 198 // Log if user was connected at the time of switching off. 199 mMetricsFeatureProvider.action(getContext(), 200 MetricsProto.MetricsEvent.ACTION_WIFI_OFF, 201 mConnectivityListener.isWifiConnected()); 202 } 203 } 204 return true; 205 case KEY_WIFI_COLLAPSE: 206 final boolean collapse = !mWifiNetworksCategory.isCollapsed(); 207 mCollapsePref.setTitle(collapse 208 ? R.string.wifi_setting_see_all : R.string.wifi_setting_see_fewer); 209 mWifiNetworksCategory.setCollapsed(collapse); 210 logEntrySelected( 211 collapse 212 ? TvSettingsEnums.NETWORK_SEE_FEWER 213 : TvSettingsEnums.NETWORK_SEE_ALL); 214 return true; 215 case KEY_WIFI_ALWAYS_SCAN: 216 Settings.Global.putInt(getActivity().getContentResolver(), 217 Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE, 218 mAlwaysScan.isChecked() ? 1 : 0); 219 logToggleInteracted( 220 TvSettingsEnums.NETWORK_ALWAYS_SCANNING_NETWORKS, mAlwaysScan.isChecked()); 221 return true; 222 case KEY_ETHERNET_STATUS: 223 return true; 224 case KEY_WIFI_ADD: 225 logEntrySelected(TvSettingsEnums.NETWORK_ADD_NEW_NETWORK); 226 mMetricsFeatureProvider.action(getActivity(), 227 MetricsProto.MetricsEvent.ACTION_WIFI_ADD_NETWORK); 228 break; 229 } 230 return super.onPreferenceTreeClick(preference); 231 } 232 isConnected()233 private boolean isConnected() { 234 NetworkInfo activeNetworkInfo = mConnectivityManager.getActiveNetworkInfo(); 235 return activeNetworkInfo != null && activeNetworkInfo.isConnected(); 236 } 237 updateConnectivity()238 private void updateConnectivity() { 239 if (!isAdded()) { 240 return; 241 } 242 243 final boolean wifiEnabled = mIsWifiHardwarePresent 244 && mConnectivityListener.isWifiEnabledOrEnabling(); 245 mEnableWifiPref.setChecked(wifiEnabled); 246 247 mWifiNetworksCategory.setVisible(wifiEnabled); 248 mCollapsePref.setVisible(wifiEnabled && mWifiNetworksCategory.shouldShowCollapsePref()); 249 mAddPref.setVisible(wifiEnabled); 250 251 if (!wifiEnabled) { 252 updateWifiList(); 253 } 254 255 int scanAlwaysAvailable = 0; 256 try { 257 scanAlwaysAvailable = Settings.Global.getInt(getContext().getContentResolver(), 258 Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE); 259 } catch (Settings.SettingNotFoundException e) { 260 // Ignore 261 } 262 mAlwaysScan.setChecked(scanAlwaysAvailable == 1); 263 264 final boolean ethernetAvailable = mConnectivityListener.isEthernetAvailable(); 265 mEthernetCategory.setVisible(ethernetAvailable); 266 mEthernetStatusPref.setVisible(ethernetAvailable); 267 mEthernetProxyPref.setVisible(ethernetAvailable); 268 mEthernetProxyPref.setOnPreferenceClickListener( 269 preference -> { 270 logEntrySelected(TvSettingsEnums.NETWORK_ETHERNET_PROXY_SETTINGS); 271 return false; 272 }); 273 mEthernetDhcpPref.setVisible(ethernetAvailable); 274 mEthernetDhcpPref.setOnPreferenceClickListener( 275 preference -> { 276 logEntrySelected(TvSettingsEnums.NETWORK_ETHERNET_IP_SETTINGS); 277 return false; 278 }); 279 280 if (ethernetAvailable) { 281 final boolean ethernetConnected = 282 mConnectivityListener.isEthernetConnected(); 283 mEthernetStatusPref.setTitle(ethernetConnected 284 ? R.string.connected : R.string.not_connected); 285 mEthernetStatusPref.setSummary(mConnectivityListener.getEthernetIpAddress()); 286 } 287 } 288 updateWifiList()289 private void updateWifiList() { 290 if (!isAdded()) { 291 return; 292 } 293 294 if (!mIsWifiHardwarePresent || !mConnectivityListener.isWifiEnabledOrEnabling()) { 295 mWifiNetworksCategory.removeAll(); 296 mNoWifiUpdateBeforeMillis = 0; 297 return; 298 } 299 300 final long now = SystemClock.elapsedRealtime(); 301 if (mNoWifiUpdateBeforeMillis > now) { 302 mHandler.removeCallbacks(mInitialUpdateWifiListRunnable); 303 mHandler.postDelayed(mInitialUpdateWifiListRunnable, 304 mNoWifiUpdateBeforeMillis - now); 305 return; 306 } 307 308 final int existingCount = mWifiNetworksCategory.getRealPreferenceCount(); 309 final Set<Preference> toRemove = new HashSet<>(existingCount); 310 for (int i = 0; i < existingCount; i++) { 311 toRemove.add(mWifiNetworksCategory.getPreference(i)); 312 } 313 314 final Context themedContext = getPreferenceManager().getContext(); 315 final Collection<AccessPoint> accessPoints = mConnectivityListener.getAvailableNetworks(); 316 int index = 0; 317 318 for (final AccessPoint accessPoint : accessPoints) { 319 accessPoint.setListener(this); 320 AccessPointPreference pref = (AccessPointPreference) accessPoint.getTag(); 321 if (pref == null) { 322 pref = new AccessPointPreference(accessPoint, themedContext, mUserBadgeCache, 323 false); 324 accessPoint.setTag(pref); 325 } else { 326 toRemove.remove(pref); 327 } 328 if (accessPoint.isActive() && !isCaptivePortal(accessPoint)) { 329 pref.setFragment(WifiDetailsFragment.class.getName()); 330 // No need to track entry selection as new page will be focused 331 pref.setOnPreferenceClickListener(preference -> false); 332 WifiDetailsFragment.prepareArgs(pref.getExtras(), accessPoint); 333 pref.setIntent(null); 334 } else { 335 pref.setFragment(null); 336 pref.setIntent(WifiConnectionActivity.createIntent(getContext(), accessPoint)); 337 pref.setOnPreferenceClickListener( 338 preference -> { 339 logEntrySelected(TvSettingsEnums.NETWORK_NOT_CONNECTED_AP); 340 return false; 341 }); 342 } 343 pref.setOrder(index++); 344 // Double-adding is harmless 345 mWifiNetworksCategory.addPreference(pref); 346 } 347 348 for (final Preference preference : toRemove) { 349 mWifiNetworksCategory.removePreference(preference); 350 } 351 352 mCollapsePref.setVisible(mWifiNetworksCategory.shouldShowCollapsePref()); 353 } 354 isCaptivePortal(AccessPoint accessPoint)355 private boolean isCaptivePortal(AccessPoint accessPoint) { 356 if (accessPoint.getDetailedState() != NetworkInfo.DetailedState.CONNECTED) { 357 return false; 358 } 359 NetworkCapabilities nc = mConnectivityManager.getNetworkCapabilities( 360 mWifiManager.getCurrentNetwork()); 361 return nc != null && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL); 362 } 363 364 @Override onConnectivityChange()365 public void onConnectivityChange() { 366 updateConnectivity(); 367 } 368 369 @Override onWifiListChanged()370 public void onWifiListChanged() { 371 updateWifiList(); 372 } 373 374 @Override onAccessPointChanged(AccessPoint accessPoint)375 public void onAccessPointChanged(AccessPoint accessPoint) { 376 ((AccessPointPreference) accessPoint.getTag()).refresh(); 377 } 378 379 @Override onLevelChanged(AccessPoint accessPoint)380 public void onLevelChanged(AccessPoint accessPoint) { 381 ((AccessPointPreference) accessPoint.getTag()).onLevelChanged(); 382 } 383 384 @Override getMetricsCategory()385 public int getMetricsCategory() { 386 return MetricsProto.MetricsEvent.SETTINGS_NETWORK_CATEGORY; 387 } 388 389 @Override getPageId()390 protected int getPageId() { 391 return TvSettingsEnums.NETWORK; 392 } 393 } 394