• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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