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 
21 import android.app.tvsettings.TvSettingsEnums;
22 import android.content.Context;
23 import android.net.IpConfiguration.IpAssignment;
24 import android.net.IpConfiguration.ProxySettings;
25 import android.net.wifi.WifiConfiguration;
26 import android.net.wifi.WifiInfo;
27 import android.net.wifi.WifiManager;
28 import android.os.Bundle;
29 import android.text.TextUtils;
30 
31 import androidx.annotation.NonNull;
32 import androidx.leanback.app.GuidedStepFragment;
33 import androidx.leanback.widget.GuidanceStylist;
34 import androidx.leanback.widget.GuidedAction;
35 import androidx.preference.ListPreference;
36 import androidx.preference.Preference;
37 
38 import com.android.internal.logging.nano.MetricsProto;
39 import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
40 import com.android.settingslib.wifi.AccessPoint;
41 import com.android.tv.settings.R;
42 import com.android.tv.settings.SettingsPreferenceFragment;
43 
44 import java.util.List;
45 
46 /**
47  * Fragment for displaying the details of a single wifi network
48  */
49 public class WifiDetailsFragment extends SettingsPreferenceFragment
50         implements ConnectivityListener.Listener, ConnectivityListener.WifiNetworkListener {
51 
52     private static final String ARG_ACCESS_POINT_STATE = "apBundle";
53 
54     private static final String KEY_CONNECTION_STATUS = "connection_status";
55     private static final String KEY_IP_ADDRESS = "ip_address";
56     private static final String KEY_MAC_ADDRESS = "mac_address";
57     private static final String KEY_SIGNAL_STRENGTH = "signal_strength";
58     private static final String KEY_RANDOM_MAC = "random_mac";
59     private static final String KEY_PROXY_SETTINGS = "proxy_settings";
60     private static final String KEY_IP_SETTINGS = "ip_settings";
61     private static final String KEY_FORGET_NETWORK = "forget_network";
62 
63     private static final String VALUE_MAC_RANDOM = "random";
64     private static final String VALUE_MAC_DEVICE = "device";
65 
66     private Preference mConnectionStatusPref;
67     private Preference mIpAddressPref;
68     private Preference mMacAddressPref;
69     private Preference mSignalStrengthPref;
70     private ListPreference mRandomMacPref;
71     private Preference mProxySettingsPref;
72     private Preference mIpSettingsPref;
73     private Preference mForgetNetworkPref;
74 
75     private ConnectivityListener mConnectivityListener;
76     private AccessPoint mAccessPoint;
77 
prepareArgs(@onNull Bundle args, AccessPoint accessPoint)78     public static void prepareArgs(@NonNull Bundle args, AccessPoint accessPoint) {
79         final Bundle apBundle = new Bundle();
80         accessPoint.saveWifiState(apBundle);
81         args.putParcelable(ARG_ACCESS_POINT_STATE, apBundle);
82     }
83 
84     @Override
getMetricsCategory()85     public int getMetricsCategory() {
86         return MetricsProto.MetricsEvent.WIFI_NETWORK_DETAILS;
87     }
88 
89     @Override
onCreate(Bundle savedInstanceState)90     public void onCreate(Bundle savedInstanceState) {
91         mConnectivityListener = new ConnectivityListener(getContext(), this, getLifecycle());
92         mAccessPoint = new AccessPoint(getContext(),
93                 getArguments().getBundle(ARG_ACCESS_POINT_STATE));
94         super.onCreate(savedInstanceState);
95     }
96 
97     @Override
onStart()98     public void onStart() {
99         super.onStart();
100         mConnectivityListener.setWifiListener(this);
101     }
102 
103     @Override
onResume()104     public void onResume() {
105         super.onResume();
106         update();
107     }
108 
109     @Override
onCreatePreferences(Bundle savedInstanceState, String rootKey)110     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
111         setPreferencesFromResource(R.xml.wifi_details, null);
112 
113         getPreferenceScreen().setTitle(mAccessPoint.getSsid());
114 
115         mConnectionStatusPref = findPreference(KEY_CONNECTION_STATUS);
116         mIpAddressPref = findPreference(KEY_IP_ADDRESS);
117         mMacAddressPref = findPreference(KEY_MAC_ADDRESS);
118         mSignalStrengthPref = findPreference(KEY_SIGNAL_STRENGTH);
119         mRandomMacPref = (ListPreference) findPreference(KEY_RANDOM_MAC);
120         mProxySettingsPref = findPreference(KEY_PROXY_SETTINGS);
121         mIpSettingsPref = findPreference(KEY_IP_SETTINGS);
122         mForgetNetworkPref = findPreference(KEY_FORGET_NETWORK);
123     }
124 
125     @Override
onConnectivityChange()126     public void onConnectivityChange() {
127         update();
128     }
129 
130     @Override
onWifiListChanged()131     public void onWifiListChanged() {
132         final List<AccessPoint> accessPoints = mConnectivityListener.getAvailableNetworks();
133         for (final AccessPoint accessPoint : accessPoints) {
134             if (TextUtils.equals(mAccessPoint.getSsidStr(), accessPoint.getSsidStr())
135                     && mAccessPoint.getSecurity() == accessPoint.getSecurity()) {
136                 // Make sure we're not holding on to the one we inflated from the bundle, because
137                 // it won't be updated
138                 mAccessPoint = accessPoint;
139                 break;
140             }
141         }
142         update();
143     }
144 
update()145     private void update() {
146         if (!isAdded()) {
147             return;
148         }
149 
150         final boolean active = mAccessPoint.isActive();
151 
152         mConnectionStatusPref.setSummary(active ? R.string.connected : R.string.not_connected);
153         mIpAddressPref.setVisible(active);
154         mSignalStrengthPref.setVisible(active);
155 
156         if (active) {
157             mIpAddressPref.setSummary(mConnectivityListener.getWifiIpAddress());
158             mSignalStrengthPref.setSummary(getSignalStrength());
159         }
160 
161         // Mac address related Preferences (info entry and random mac setting entry)
162         String macAddress = mConnectivityListener.getWifiMacAddress(mAccessPoint);
163         if (active && !TextUtils.isEmpty(macAddress)) {
164             mMacAddressPref.setVisible(true);
165             updateMacAddressPref(macAddress);
166             updateRandomMacPref();
167         } else {
168             mMacAddressPref.setVisible(false);
169             mRandomMacPref.setVisible(false);
170         }
171 
172         WifiConfiguration wifiConfiguration = mAccessPoint.getConfig();
173         if (wifiConfiguration != null) {
174             final int networkId = wifiConfiguration.networkId;
175             ProxySettings proxySettings = wifiConfiguration.getIpConfiguration().getProxySettings();
176             mProxySettingsPref.setSummary(proxySettings == ProxySettings.NONE
177                     ? R.string.wifi_action_proxy_none : R.string.wifi_action_proxy_manual);
178             mProxySettingsPref.setIntent(EditProxySettingsActivity.createIntent(getContext(),
179                     networkId));
180 
181             IpAssignment ipAssignment = wifiConfiguration.getIpConfiguration().getIpAssignment();
182             mIpSettingsPref.setSummary(ipAssignment == IpAssignment.STATIC
183                             ? R.string.wifi_action_static : R.string.wifi_action_dhcp);
184             mIpSettingsPref.setIntent(EditIpSettingsActivity.createIntent(getContext(), networkId));
185 
186             mForgetNetworkPref.setFragment(ForgetNetworkConfirmFragment.class.getName());
187             ForgetNetworkConfirmFragment.prepareArgs(mForgetNetworkPref.getExtras(), mAccessPoint);
188         }
189 
190         mProxySettingsPref.setVisible(wifiConfiguration != null);
191         mProxySettingsPref.setOnPreferenceClickListener(
192                 preference -> {
193                     logEntrySelected(TvSettingsEnums.NETWORK_AP_INFO_PROXY_SETTINGS);
194                     return false;
195                 });
196         mIpSettingsPref.setVisible(wifiConfiguration != null);
197         mIpSettingsPref.setOnPreferenceClickListener(
198                 preference -> {
199                     logEntrySelected(TvSettingsEnums.NETWORK_AP_INFO_IP_SETTINGS);
200                     return false;
201                 });
202         mForgetNetworkPref.setVisible(wifiConfiguration != null);
203         mForgetNetworkPref.setOnPreferenceClickListener(
204                 preference -> {
205                     logEntrySelected(TvSettingsEnums.NETWORK_AP_INFO_FORGET_NETWORK);
206                     return false;
207                 });
208     }
209 
getSignalStrength()210     private String getSignalStrength() {
211         String[] signalLevels = getResources().getStringArray(R.array.wifi_signal_strength);
212         int strength = mConnectivityListener.getWifiSignalStrength(signalLevels.length);
213         return signalLevels[strength];
214     }
215 
updateMacAddressPref(String macAddress)216     private void updateMacAddressPref(String macAddress) {
217         if (WifiInfo.DEFAULT_MAC_ADDRESS.equals(macAddress)) {
218             mMacAddressPref.setSummary(R.string.mac_address_not_available);
219         } else {
220             mMacAddressPref.setSummary(macAddress);
221         }
222         if (mAccessPoint == null || mAccessPoint.getConfig() == null) {
223             return;
224         }
225         // For saved Passpoint network, framework doesn't have the field to keep the MAC choice
226         // persistently, so Passpoint network will always use the default value so far, which is
227         // randomized MAC address, so don't need to modify title.
228         if (mAccessPoint.isPasspoint() || mAccessPoint.isPasspointConfig()) {
229             return;
230         }
231         mMacAddressPref.setTitle(
232                 (mAccessPoint.getConfig().macRandomizationSetting
233                         == WifiConfiguration.RANDOMIZATION_PERSISTENT)
234                         ? R.string.title_randomized_mac_address
235                         : R.string.title_mac_address);
236     }
237 
updateRandomMacPref()238     private void updateRandomMacPref() {
239         mRandomMacPref.setVisible(mConnectivityListener.isMacAddressRandomizationSupported());
240         boolean isMacRandomized =
241                 (mConnectivityListener.getWifiMacRandomizationSetting(mAccessPoint)
242                         == WifiConfiguration.RANDOMIZATION_PERSISTENT);
243         mRandomMacPref.setValue(isMacRandomized ? VALUE_MAC_RANDOM : VALUE_MAC_DEVICE);
244         if (mAccessPoint.isEphemeral() || mAccessPoint.isPasspoint()
245                 || mAccessPoint.isPasspointConfig()) {
246             mRandomMacPref.setSelectable(false);
247             mRandomMacPref.setSummary(R.string.mac_address_ephemeral_summary);
248         } else {
249             mRandomMacPref.setSelectable(true);
250             mRandomMacPref.setSummary(mRandomMacPref.getEntries()[isMacRandomized ? 0 : 1]);
251         }
252         mRandomMacPref.setOnPreferenceChangeListener(
253                 (pref, newValue) -> {
254                     mConnectivityListener.applyMacRandomizationSetting(
255                             mAccessPoint,
256                             VALUE_MAC_RANDOM.equals(newValue));
257                     // The above call should trigger a connectivity change which will refresh
258                     // the UI.
259                     return true;
260                 });
261     }
262 
263     @Override
getPageId()264     protected int getPageId() {
265         return TvSettingsEnums.NETWORK_AP_INFO;
266     }
267 
268     public static class ForgetNetworkConfirmFragment extends GuidedStepFragment {
269 
270         private AccessPoint mAccessPoint;
271         private final MetricsFeatureProvider mMetricsFeatureProvider = new MetricsFeatureProvider();
272 
prepareArgs(@onNull Bundle args, AccessPoint accessPoint)273         public static void prepareArgs(@NonNull Bundle args, AccessPoint accessPoint) {
274             final Bundle apBundle = new Bundle();
275             accessPoint.saveWifiState(apBundle);
276             args.putParcelable(ARG_ACCESS_POINT_STATE, apBundle);
277         }
278 
279         @Override
onCreate(Bundle savedInstanceState)280         public void onCreate(Bundle savedInstanceState) {
281             mAccessPoint = new AccessPoint(getContext(),
282                     getArguments().getBundle(ARG_ACCESS_POINT_STATE));
283             super.onCreate(savedInstanceState);
284         }
285 
286         @NonNull
287         @Override
onCreateGuidance(Bundle savedInstanceState)288         public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
289             return new GuidanceStylist.Guidance(
290                     getString(R.string.wifi_forget_network),
291                     getString(R.string.wifi_forget_network_description),
292                     mAccessPoint.getSsidStr(),
293                     getContext().getDrawable(R.drawable.ic_wifi_signal_4_white_132dp));
294         }
295 
296         @Override
onCreateActions(@onNull List<GuidedAction> actions, Bundle savedInstanceState)297         public void onCreateActions(@NonNull List<GuidedAction> actions,
298                 Bundle savedInstanceState) {
299             final Context context = getContext();
300             actions.add(new GuidedAction.Builder(context)
301                     .clickAction(GuidedAction.ACTION_ID_OK)
302                     .build());
303             actions.add(new GuidedAction.Builder(context)
304                     .clickAction(GuidedAction.ACTION_ID_CANCEL)
305                     .build());
306         }
307 
308         @Override
onGuidedActionClicked(GuidedAction action)309         public void onGuidedActionClicked(GuidedAction action) {
310             if (action.getId() == GuidedAction.ACTION_ID_OK) {
311                 WifiManager wifiManager =
312                         (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
313                 wifiManager.forget(mAccessPoint.getConfig().networkId, null);
314                 mMetricsFeatureProvider.action(
315                         getContext(), MetricsProto.MetricsEvent.ACTION_WIFI_FORGET);
316             }
317             getFragmentManager().popBackStack();
318         }
319     }
320 }
321