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.NetworkCapabilities;
33 import android.net.NetworkInfo;
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.SystemClock;
39 import android.os.UserManager;
40 import android.provider.Settings;
41 
42 import androidx.annotation.Keep;
43 import androidx.preference.Preference;
44 import androidx.preference.PreferenceCategory;
45 import androidx.preference.PreferenceManager;
46 import androidx.preference.TwoStatePreference;
47 
48 import com.android.settingslib.RestrictedPreference;
49 import com.android.settingslib.wifi.AccessPoint;
50 import com.android.tv.settings.MainFragment;
51 import com.android.tv.settings.R;
52 import com.android.tv.settings.RestrictedPreferenceAdapter;
53 import com.android.tv.settings.SettingsPreferenceFragment;
54 import com.android.tv.settings.overlay.FlavorUtils;
55 import com.android.tv.settings.util.SliceUtils;
56 import com.android.tv.settings.widget.CustomContentDescriptionSwitchPreference;
57 import com.android.tv.settings.widget.TvAccessPointPreference;
58 import com.android.tv.twopanelsettings.slices.SlicePreference;
59 
60 import java.util.Collection;
61 import java.util.HashSet;
62 import java.util.Set;
63 
64 /**
65  * Fragment for controlling network connectivity
66  */
67 @Keep
68 public class NetworkFragment extends SettingsPreferenceFragment implements
69         ConnectivityListener.Listener, ConnectivityListener.WifiNetworkListener,
70         AccessPoint.AccessPointListener {
71 
72     private static final String KEY_WIFI_ENABLE = "wifi_enable";
73     private static final String KEY_WIFI_LIST = "wifi_list";
74     private static final String KEY_WIFI_COLLAPSE = "wifi_collapse";
75     private static final String KEY_WIFI_OTHER = "wifi_other";
76     private static final String KEY_WIFI_ADD = "wifi_add";
77     private static final String KEY_WIFI_ADD_EASYCONNECT = "wifi_add_easyconnect";
78     private static final String KEY_WIFI_ALWAYS_SCAN = "wifi_always_scan";
79     private static final String KEY_ETHERNET = "ethernet";
80     private static final String KEY_ETHERNET_STATUS = "ethernet_status";
81     private static final String KEY_ETHERNET_PROXY = "ethernet_proxy";
82     private static final String KEY_ETHERNET_DHCP = "ethernet_dhcp";
83     private static final String KEY_DATA_SAVER_SLICE = "data_saver_slice";
84     private static final String KEY_DATA_ALERT_SLICE = "data_alert_slice";
85     private static final String KEY_NETWORK_DIAGNOSTICS = "network_diagnostics";
86 
87     private static final String ACTION_DATA_ALERT_SETTINGS = "android.settings.DATA_ALERT_SETTINGS";
88     private static final int INITIAL_UPDATE_DELAY = 500;
89 
90     private static final String NETWORK_DIAGNOSTICS_ACTION =
91             "com.android.tv.settings.network.NETWORK_DIAGNOSTICS";
92 
93     private ConnectivityListener mConnectivityListener;
94     private WifiManager mWifiManager;
95     private ConnectivityManager mConnectivityManager;
96     private TvAccessPointPreference.UserBadgeCache mUserBadgeCache;
97 
98     private TwoStatePreference mEnableWifiPref;
99     private CollapsibleCategory mWifiNetworksCategory;
100     private Preference mCollapsePref;
101     private RestrictedPreference mAddPref;
102     private RestrictedPreference mAddEasyConnectPref;
103     private TwoStatePreference mAlwaysScan;
104     private PreferenceCategory mEthernetCategory;
105     private Preference mEthernetStatusPref;
106     private Preference mEthernetProxyPref;
107     private Preference mEthernetDhcpPref;
108     private PreferenceCategory mWifiOther;
109 
110     private final Handler mHandler = new Handler();
111     private long mNoWifiUpdateBeforeMillis;
112     private Runnable mInitialUpdateWifiListRunnable = new Runnable() {
113         @Override
114         public void run() {
115             mNoWifiUpdateBeforeMillis = 0;
116             updateWifiList();
117         }
118     };
119     private boolean mIsWifiHardwarePresent;
120 
newInstance()121     public static NetworkFragment newInstance() {
122         return new NetworkFragment();
123     }
124 
125     @Override
onCreate(Bundle savedInstanceState)126     public void onCreate(Bundle savedInstanceState) {
127         mIsWifiHardwarePresent = getContext().getPackageManager()
128                 .hasSystemFeature(PackageManager.FEATURE_WIFI);
129         mConnectivityListener = new ConnectivityListener(
130                 getContext(), this, getSettingsLifecycle());
131         mWifiManager = getContext().getSystemService(WifiManager.class);
132         mConnectivityManager = getContext().getSystemService(ConnectivityManager.class);
133         mUserBadgeCache =
134                 new TvAccessPointPreference.UserBadgeCache(getContext().getPackageManager());
135         super.onCreate(savedInstanceState);
136     }
137 
138     @Override
onStart()139     public void onStart() {
140         super.onStart();
141         mConnectivityListener.setWifiListener(this);
142         mNoWifiUpdateBeforeMillis = SystemClock.elapsedRealtime() + INITIAL_UPDATE_DELAY;
143         updateWifiList();
144     }
145 
146     @Override
onDestroy()147     public void onDestroy() {
148         super.onDestroy();
149     }
150 
151     @Override
onResume()152     public void onResume() {
153         super.onResume();
154         // There doesn't seem to be an API to listen to everything this could cover, so
155         // tickle it here and hope for the best.
156         updateConnectivity();
157     }
158 
getPreferenceScreenResId()159     private int getPreferenceScreenResId() {
160         switch (FlavorUtils.getFlavor(getContext())) {
161             case FLAVOR_CLASSIC:
162             case FLAVOR_TWO_PANEL:
163                 return R.xml.network;
164             case FLAVOR_X:
165             case FLAVOR_VENDOR:
166                 return R.xml.network_x;
167             default:
168                 return R.xml.network;
169         }
170     }
171 
172     @Override
onCreatePreferences(Bundle savedInstanceState, String rootKey)173     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
174         getPreferenceManager().setPreferenceComparisonCallback(
175                 new PreferenceManager.SimplePreferenceComparisonCallback());
176         setPreferencesFromResource(getPreferenceScreenResId(), null);
177 
178         mEnableWifiPref = (TwoStatePreference) findPreference(KEY_WIFI_ENABLE);
179         mWifiNetworksCategory = (CollapsibleCategory) findPreference(KEY_WIFI_LIST);
180         mCollapsePref = findPreference(KEY_WIFI_COLLAPSE);
181         mAddPref = (RestrictedPreference) findPreference(KEY_WIFI_ADD);
182         mAddEasyConnectPref = (RestrictedPreference) findPreference(KEY_WIFI_ADD_EASYCONNECT);
183         mAlwaysScan = (TwoStatePreference) findPreference(KEY_WIFI_ALWAYS_SCAN);
184         mWifiOther = (PreferenceCategory) findPreference(KEY_WIFI_OTHER);
185 
186         mEthernetCategory = (PreferenceCategory) findPreference(KEY_ETHERNET);
187         mEthernetStatusPref = findPreference(KEY_ETHERNET_STATUS);
188         mEthernetProxyPref = findPreference(KEY_ETHERNET_PROXY);
189         mEthernetProxyPref.setIntent(EditProxySettingsActivity.createIntent(getContext(),
190                 WifiConfiguration.INVALID_NETWORK_ID));
191         mEthernetDhcpPref = findPreference(KEY_ETHERNET_DHCP);
192         mEthernetDhcpPref.setIntent(EditIpSettingsActivity.createIntent(getContext(),
193                 WifiConfiguration.INVALID_NETWORK_ID));
194 
195         if (!mIsWifiHardwarePresent) {
196             mEnableWifiPref.setVisible(false);
197         }
198 
199         updateVisibilityForDataSaver();
200         Preference dataSaverSlicePref = findPreference(KEY_DATA_SAVER_SLICE);
201         Preference dataAlertSlicePref = findPreference(KEY_DATA_ALERT_SLICE);
202         Intent i = getActivity().getIntent();
203         if (i != null && i.getAction() != null) {
204             if (i.getAction().equals(Settings.ACTION_DATA_SAVER_SETTINGS)
205                     && dataSaverSlicePref.isVisible()) {
206                 mHandler.post(() -> scrollToPreference(dataSaverSlicePref));
207             } else if (i.getAction().equals(ACTION_DATA_ALERT_SETTINGS)
208                     && dataAlertSlicePref.isVisible()) {
209                 mHandler.post(() -> scrollToPreference(dataAlertSlicePref));
210             }
211         }
212 
213         Preference networkDiagnosticsPref = findPreference(KEY_NETWORK_DIAGNOSTICS);
214         Intent networkDiagnosticsIntent = makeNetworkDiagnosticsIntent();
215         if (networkDiagnosticsIntent != null) {
216             networkDiagnosticsPref.setVisible(true);
217             networkDiagnosticsPref.setIntent(networkDiagnosticsIntent);
218         } else {
219             networkDiagnosticsPref.setVisible(false);
220         }
221 
222         final UserManager userManager = UserManager.get(getContext());
223         if (userManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI)) {
224             mAddPref.setFragment(null);
225             mAddEasyConnectPref.setFragment(null);
226 
227             if (!mAddPref.isDisabledByAdmin()) {
228                 mAddPref.setEnabled(false);
229             }
230             if (!mAddEasyConnectPref.isDisabledByAdmin()) {
231                 mAddEasyConnectPref.setEnabled(false);
232             }
233         }
234     }
235 
updateVisibilityForDataSaver()236     private void updateVisibilityForDataSaver() {
237         Preference dataSaverSlicePref = findPreference(KEY_DATA_SAVER_SLICE);
238         Preference dataAlertSlicePref = findPreference(KEY_DATA_ALERT_SLICE);
239         boolean isDataSaverVisible = isConnected() && SliceUtils.isSliceProviderValid(
240                 getContext(), ((SlicePreference) dataSaverSlicePref).getUri());
241         boolean isDataAlertVisible = isConnected() && SliceUtils.isSliceProviderValid(
242                 getContext(), ((SlicePreference) dataAlertSlicePref).getUri());
243         dataSaverSlicePref.setVisible(isDataSaverVisible);
244         dataAlertSlicePref.setVisible(isDataAlertVisible);
245     }
246 
247     @Override
onPreferenceTreeClick(Preference preference)248     public boolean onPreferenceTreeClick(Preference preference) {
249         if (preference.getKey() == null) {
250             return super.onPreferenceTreeClick(preference);
251         }
252         switch (preference.getKey()) {
253             case KEY_WIFI_ENABLE:
254                 mConnectivityListener.setWifiEnabled(mEnableWifiPref.isChecked());
255                 logToggleInteracted(
256                         TvSettingsEnums.NETWORK_WIFI_ON_OFF, mEnableWifiPref.isChecked());
257                 return true;
258             case KEY_WIFI_COLLAPSE:
259                 final boolean collapse = !mWifiNetworksCategory.isCollapsed();
260                 mCollapsePref.setTitle(collapse
261                         ? R.string.wifi_setting_see_all : R.string.wifi_setting_see_fewer);
262                 mWifiNetworksCategory.setCollapsed(collapse);
263                 logEntrySelected(
264                         collapse
265                                 ? TvSettingsEnums.NETWORK_SEE_FEWER
266                                 : TvSettingsEnums.NETWORK_SEE_ALL);
267                 return true;
268             case KEY_WIFI_ALWAYS_SCAN:
269                 Settings.Global.putInt(getActivity().getContentResolver(),
270                         Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE,
271                         mAlwaysScan.isChecked() ? 1 : 0);
272                 logToggleInteracted(
273                         TvSettingsEnums.NETWORK_ALWAYS_SCANNING_NETWORKS, mAlwaysScan.isChecked());
274                 return true;
275             case KEY_ETHERNET_STATUS:
276                 return true;
277             case KEY_WIFI_ADD:
278                 logEntrySelected(TvSettingsEnums.NETWORK_ADD_NEW_NETWORK);
279                 break;
280             case KEY_WIFI_ADD_EASYCONNECT:
281                 startActivity(AddWifiNetworkActivity.createEasyConnectIntent(getContext()));
282                 break;
283         }
284         return super.onPreferenceTreeClick(preference);
285     }
286 
isConnected()287     private boolean isConnected() {
288         NetworkInfo activeNetworkInfo = mConnectivityManager.getActiveNetworkInfo();
289         return activeNetworkInfo != null && activeNetworkInfo.isConnected()
290                 && ConnectivityManager.TYPE_ETHERNET != activeNetworkInfo.getType();
291     }
292 
updateConnectivity()293     private void updateConnectivity() {
294         if (!isAdded()) {
295             return;
296         }
297 
298         final boolean wifiEnabled = mIsWifiHardwarePresent
299                 && mConnectivityListener.isWifiEnabledOrEnabling();
300         mEnableWifiPref.setChecked(wifiEnabled);
301 
302         mWifiNetworksCategory.setVisible(wifiEnabled);
303         mCollapsePref.setVisible(wifiEnabled && mWifiNetworksCategory.shouldShowCollapsePref());
304         mAddPref.setVisible(wifiEnabled);
305         if (mAddEasyConnectPref != null) {
306             mAddEasyConnectPref.setVisible(wifiEnabled && mWifiManager.isEasyConnectSupported());
307         }
308 
309         if (!wifiEnabled) {
310             updateWifiList();
311         }
312 
313         int scanAlwaysAvailable = 0;
314         try {
315             scanAlwaysAvailable = Settings.Global.getInt(getContext().getContentResolver(),
316                     Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE);
317         } catch (Settings.SettingNotFoundException e) {
318             // Ignore
319         }
320         mAlwaysScan.setChecked(scanAlwaysAvailable == 1);
321         if (mAlwaysScan instanceof CustomContentDescriptionSwitchPreference) {
322             ((CustomContentDescriptionSwitchPreference) mAlwaysScan).setContentDescription(
323                     getResources()
324                             .getString(
325                                     R.string.wifi_setting_always_scan_content_description));
326         }
327 
328         final boolean ethernetAvailable = mConnectivityListener.isEthernetAvailable();
329         mEthernetCategory.setVisible(ethernetAvailable);
330         mEthernetStatusPref.setVisible(ethernetAvailable);
331         mEthernetProxyPref.setVisible(ethernetAvailable);
332         mEthernetProxyPref.setOnPreferenceClickListener(
333                 preference -> {
334                     logEntrySelected(TvSettingsEnums.NETWORK_ETHERNET_PROXY_SETTINGS);
335                     return false;
336                 });
337         mEthernetDhcpPref.setVisible(ethernetAvailable);
338         mEthernetDhcpPref.setOnPreferenceClickListener(
339                 preference -> {
340                     logEntrySelected(TvSettingsEnums.NETWORK_ETHERNET_IP_SETTINGS);
341                     return false;
342                 });
343 
344         if (ethernetAvailable) {
345             final boolean ethernetConnected =
346                     mConnectivityListener.isEthernetConnected();
347             mEthernetStatusPref.setTitle(ethernetConnected
348                     ? R.string.connected : R.string.not_connected);
349             mEthernetStatusPref.setSummary(mConnectivityListener.getEthernetIpAddress());
350         }
351 
352         updateVisibilityForDataSaver();
353     }
354 
updateWifiList()355     private void updateWifiList() {
356         if (!isAdded()) {
357             return;
358         }
359 
360         if (!mIsWifiHardwarePresent || !mConnectivityListener.isWifiEnabledOrEnabling()) {
361             mWifiNetworksCategory.removeAll();
362             mNoWifiUpdateBeforeMillis = 0;
363             return;
364         }
365 
366         final long now = SystemClock.elapsedRealtime();
367         if (mNoWifiUpdateBeforeMillis > now) {
368             mHandler.removeCallbacks(mInitialUpdateWifiListRunnable);
369             mHandler.postDelayed(mInitialUpdateWifiListRunnable,
370                     mNoWifiUpdateBeforeMillis - now);
371             return;
372         }
373 
374         final int existingCount = mWifiNetworksCategory.getRealPreferenceCount();
375         final Set<Preference> toRemove = new HashSet<>(existingCount);
376         for (int i = 0; i < existingCount; i++) {
377             toRemove.add(mWifiNetworksCategory.getPreference(i));
378         }
379 
380         final Context themedContext = getPreferenceManager().getContext();
381         final Collection<AccessPoint> accessPoints = mConnectivityListener.getAvailableNetworks();
382         int index = 0;
383 
384         for (final AccessPoint accessPoint : accessPoints) {
385             accessPoint.setListener(this);
386             RestrictedPreferenceAdapter<TvAccessPointPreference> restrictedPref =
387                     (RestrictedPreferenceAdapter<TvAccessPointPreference>) accessPoint.getTag();
388             Preference pref;
389             if (restrictedPref == null) {
390                 pref = new TvAccessPointPreference(accessPoint, themedContext, mUserBadgeCache,
391                         false);
392                 restrictedPref = new RestrictedPreferenceAdapter(themedContext, pref,
393                         UserManager.DISALLOW_CONFIG_WIFI);
394                 accessPoint.setTag(restrictedPref);
395             } else {
396                 toRemove.remove(restrictedPref.getPreference());
397                 pref = restrictedPref.getOriginalPreference();
398             }
399             if (accessPoint.isActive() && !isCaptivePortal(accessPoint)) {
400                 pref.setFragment(WifiDetailsFragment.class.getName());
401                 // No need to track entry selection as new page will be focused
402                 pref.setOnPreferenceClickListener(preference -> false);
403                 WifiDetailsFragment.prepareArgs(pref.getExtras(), accessPoint);
404                 pref.setIntent(null);
405             } else {
406                 pref.setFragment(null);
407                 pref.setIntent(WifiConnectionActivity.createIntent(getContext(), accessPoint));
408                 pref.setOnPreferenceClickListener(
409                         preference -> {
410                             logEntrySelected(TvSettingsEnums.NETWORK_NOT_CONNECTED_AP);
411                             return false;
412                         });
413             }
414             pref.setVisible(!restrictedPref.isRestricted() || accessPoint.isSaved());
415             pref.setOrder(index++);
416             restrictedPref.updatePreference();
417 
418             // Double-adding is harmless
419             mWifiNetworksCategory.addPreference(restrictedPref.getPreference());
420         }
421 
422         for (final Preference preference : toRemove) {
423             mWifiNetworksCategory.removePreference(preference);
424         }
425 
426         mCollapsePref.setVisible(mWifiNetworksCategory.shouldShowCollapsePref());
427     }
428 
isCaptivePortal(AccessPoint accessPoint)429     private boolean isCaptivePortal(AccessPoint accessPoint) {
430         if (accessPoint.getDetailedState() != NetworkInfo.DetailedState.CONNECTED) {
431             return false;
432         }
433         NetworkCapabilities nc = mConnectivityManager.getNetworkCapabilities(
434                 mWifiManager.getCurrentNetwork());
435         return nc != null && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
436     }
437 
makeNetworkDiagnosticsIntent()438     private Intent makeNetworkDiagnosticsIntent() {
439         Intent intent = new Intent();
440         intent.setAction(NETWORK_DIAGNOSTICS_ACTION);
441 
442         ResolveInfo resolveInfo = MainFragment.systemIntentIsHandled(getContext(), intent);
443         if (resolveInfo == null || resolveInfo.activityInfo == null) {
444             return null;
445         }
446 
447         intent.setPackage(resolveInfo.activityInfo.packageName);
448 
449         return intent;
450     }
451 
452     @Override
onConnectivityChange()453     public void onConnectivityChange() {
454         updateConnectivity();
455     }
456 
457     @Override
onWifiListChanged()458     public void onWifiListChanged() {
459         updateWifiList();
460     }
461 
462     @Override
onAccessPointChanged(AccessPoint accessPoint)463     public void onAccessPointChanged(AccessPoint accessPoint) {
464         RestrictedPreferenceAdapter<TvAccessPointPreference> restrictedPref =
465                 (RestrictedPreferenceAdapter<TvAccessPointPreference>) accessPoint.getTag();
466         restrictedPref.updatePreference(pref -> pref.refresh());
467     }
468 
469     @Override
onLevelChanged(AccessPoint accessPoint)470     public void onLevelChanged(AccessPoint accessPoint) {
471         RestrictedPreferenceAdapter<TvAccessPointPreference> restrictedPref =
472                 (RestrictedPreferenceAdapter<TvAccessPointPreference>) accessPoint.getTag();
473         restrictedPref.updatePreference(pref -> pref.onLevelChanged());
474     }
475 
476     @Override
getPageId()477     protected int getPageId() {
478         return TvSettingsEnums.NETWORK;
479     }
480 }
481