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