1 /* 2 * Copyright (C) 2010 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.settings.wifi; 18 19 import android.content.Context; 20 import android.net.NetworkInfo.DetailedState; 21 import android.net.wifi.ScanResult; 22 import android.net.wifi.WifiConfiguration; 23 import android.net.wifi.WifiConfiguration.KeyMgmt; 24 import android.net.wifi.WifiInfo; 25 import android.net.wifi.WifiManager; 26 import android.os.Bundle; 27 import android.preference.Preference; 28 import android.util.Log; 29 import android.view.View; 30 import android.widget.ImageView; 31 32 import com.android.settings.R; 33 34 class AccessPoint extends Preference { 35 static final String TAG = "Settings.AccessPoint"; 36 37 private static final String KEY_DETAILEDSTATE = "key_detailedstate"; 38 private static final String KEY_WIFIINFO = "key_wifiinfo"; 39 private static final String KEY_SCANRESULT = "key_scanresult"; 40 private static final String KEY_CONFIG = "key_config"; 41 42 private static final int[] STATE_SECURED = { 43 R.attr.state_encrypted 44 }; 45 private static final int[] STATE_NONE = {}; 46 47 /** These values are matched in string arrays -- changes must be kept in sync */ 48 static final int SECURITY_NONE = 0; 49 static final int SECURITY_WEP = 1; 50 static final int SECURITY_PSK = 2; 51 static final int SECURITY_EAP = 3; 52 53 enum PskType { 54 UNKNOWN, 55 WPA, 56 WPA2, 57 WPA_WPA2 58 } 59 60 String ssid; 61 String bssid; 62 int security; 63 int networkId; 64 boolean wpsAvailable = false; 65 66 PskType pskType = PskType.UNKNOWN; 67 68 private WifiConfiguration mConfig; 69 /* package */ScanResult mScanResult; 70 71 private int mRssi; 72 private WifiInfo mInfo; 73 private DetailedState mState; 74 getSecurity(WifiConfiguration config)75 static int getSecurity(WifiConfiguration config) { 76 if (config.allowedKeyManagement.get(KeyMgmt.WPA_PSK)) { 77 return SECURITY_PSK; 78 } 79 if (config.allowedKeyManagement.get(KeyMgmt.WPA_EAP) || 80 config.allowedKeyManagement.get(KeyMgmt.IEEE8021X)) { 81 return SECURITY_EAP; 82 } 83 return (config.wepKeys[0] != null) ? SECURITY_WEP : SECURITY_NONE; 84 } 85 getSecurity(ScanResult result)86 private static int getSecurity(ScanResult result) { 87 if (result.capabilities.contains("WEP")) { 88 return SECURITY_WEP; 89 } else if (result.capabilities.contains("PSK")) { 90 return SECURITY_PSK; 91 } else if (result.capabilities.contains("EAP")) { 92 return SECURITY_EAP; 93 } 94 return SECURITY_NONE; 95 } 96 getSecurityString(boolean concise)97 public String getSecurityString(boolean concise) { 98 Context context = getContext(); 99 switch(security) { 100 case SECURITY_EAP: 101 return concise ? context.getString(R.string.wifi_security_short_eap) : 102 context.getString(R.string.wifi_security_eap); 103 case SECURITY_PSK: 104 switch (pskType) { 105 case WPA: 106 return concise ? context.getString(R.string.wifi_security_short_wpa) : 107 context.getString(R.string.wifi_security_wpa); 108 case WPA2: 109 return concise ? context.getString(R.string.wifi_security_short_wpa2) : 110 context.getString(R.string.wifi_security_wpa2); 111 case WPA_WPA2: 112 return concise ? context.getString(R.string.wifi_security_short_wpa_wpa2) : 113 context.getString(R.string.wifi_security_wpa_wpa2); 114 case UNKNOWN: 115 default: 116 return concise ? context.getString(R.string.wifi_security_short_psk_generic) 117 : context.getString(R.string.wifi_security_psk_generic); 118 } 119 case SECURITY_WEP: 120 return concise ? context.getString(R.string.wifi_security_short_wep) : 121 context.getString(R.string.wifi_security_wep); 122 case SECURITY_NONE: 123 default: 124 return concise ? "" : context.getString(R.string.wifi_security_none); 125 } 126 } 127 getPskType(ScanResult result)128 private static PskType getPskType(ScanResult result) { 129 boolean wpa = result.capabilities.contains("WPA-PSK"); 130 boolean wpa2 = result.capabilities.contains("WPA2-PSK"); 131 if (wpa2 && wpa) { 132 return PskType.WPA_WPA2; 133 } else if (wpa2) { 134 return PskType.WPA2; 135 } else if (wpa) { 136 return PskType.WPA; 137 } else { 138 Log.w(TAG, "Received abnormal flag string: " + result.capabilities); 139 return PskType.UNKNOWN; 140 } 141 } 142 AccessPoint(Context context, WifiConfiguration config)143 AccessPoint(Context context, WifiConfiguration config) { 144 super(context); 145 setWidgetLayoutResource(R.layout.preference_widget_wifi_signal); 146 loadConfig(config); 147 refresh(); 148 } 149 AccessPoint(Context context, ScanResult result)150 AccessPoint(Context context, ScanResult result) { 151 super(context); 152 setWidgetLayoutResource(R.layout.preference_widget_wifi_signal); 153 loadResult(result); 154 refresh(); 155 } 156 AccessPoint(Context context, Bundle savedState)157 AccessPoint(Context context, Bundle savedState) { 158 super(context); 159 setWidgetLayoutResource(R.layout.preference_widget_wifi_signal); 160 161 mConfig = savedState.getParcelable(KEY_CONFIG); 162 if (mConfig != null) { 163 loadConfig(mConfig); 164 } 165 mScanResult = (ScanResult) savedState.getParcelable(KEY_SCANRESULT); 166 if (mScanResult != null) { 167 loadResult(mScanResult); 168 } 169 mInfo = (WifiInfo) savedState.getParcelable(KEY_WIFIINFO); 170 if (savedState.containsKey(KEY_DETAILEDSTATE)) { 171 mState = DetailedState.valueOf(savedState.getString(KEY_DETAILEDSTATE)); 172 } 173 update(mInfo, mState); 174 } 175 saveWifiState(Bundle savedState)176 public void saveWifiState(Bundle savedState) { 177 savedState.putParcelable(KEY_CONFIG, mConfig); 178 savedState.putParcelable(KEY_SCANRESULT, mScanResult); 179 savedState.putParcelable(KEY_WIFIINFO, mInfo); 180 if (mState != null) { 181 savedState.putString(KEY_DETAILEDSTATE, mState.toString()); 182 } 183 } 184 loadConfig(WifiConfiguration config)185 private void loadConfig(WifiConfiguration config) { 186 ssid = (config.SSID == null ? "" : removeDoubleQuotes(config.SSID)); 187 bssid = config.BSSID; 188 security = getSecurity(config); 189 networkId = config.networkId; 190 mRssi = Integer.MAX_VALUE; 191 mConfig = config; 192 } 193 loadResult(ScanResult result)194 private void loadResult(ScanResult result) { 195 ssid = result.SSID; 196 bssid = result.BSSID; 197 security = getSecurity(result); 198 wpsAvailable = security != SECURITY_EAP && result.capabilities.contains("WPS"); 199 if (security == SECURITY_PSK) 200 pskType = getPskType(result); 201 networkId = -1; 202 mRssi = result.level; 203 mScanResult = result; 204 } 205 206 @Override onBindView(View view)207 protected void onBindView(View view) { 208 super.onBindView(view); 209 ImageView signal = (ImageView) view.findViewById(R.id.signal); 210 if (mRssi == Integer.MAX_VALUE) { 211 signal.setImageDrawable(null); 212 } else { 213 signal.setImageLevel(getLevel()); 214 signal.setImageResource(R.drawable.wifi_signal); 215 signal.setImageState((security != SECURITY_NONE) ? 216 STATE_SECURED : STATE_NONE, true); 217 } 218 } 219 220 @Override compareTo(Preference preference)221 public int compareTo(Preference preference) { 222 if (!(preference instanceof AccessPoint)) { 223 return 1; 224 } 225 AccessPoint other = (AccessPoint) preference; 226 // Active one goes first. 227 if (mInfo != other.mInfo) { 228 return (mInfo != null) ? -1 : 1; 229 } 230 // Reachable one goes before unreachable one. 231 if ((mRssi ^ other.mRssi) < 0) { 232 return (mRssi != Integer.MAX_VALUE) ? -1 : 1; 233 } 234 // Configured one goes before unconfigured one. 235 if ((networkId ^ other.networkId) < 0) { 236 return (networkId != -1) ? -1 : 1; 237 } 238 // Sort by signal strength. 239 int difference = WifiManager.compareSignalLevel(other.mRssi, mRssi); 240 if (difference != 0) { 241 return difference; 242 } 243 // Sort by ssid. 244 return ssid.compareToIgnoreCase(other.ssid); 245 } 246 update(ScanResult result)247 boolean update(ScanResult result) { 248 if (ssid.equals(result.SSID) && security == getSecurity(result)) { 249 if (WifiManager.compareSignalLevel(result.level, mRssi) > 0) { 250 int oldLevel = getLevel(); 251 mRssi = result.level; 252 if (getLevel() != oldLevel) { 253 notifyChanged(); 254 } 255 } 256 // This flag only comes from scans, is not easily saved in config 257 if (security == SECURITY_PSK) { 258 pskType = getPskType(result); 259 } 260 refresh(); 261 return true; 262 } 263 return false; 264 } 265 update(WifiInfo info, DetailedState state)266 void update(WifiInfo info, DetailedState state) { 267 boolean reorder = false; 268 if (info != null && networkId != WifiConfiguration.INVALID_NETWORK_ID 269 && networkId == info.getNetworkId()) { 270 reorder = (mInfo == null); 271 mRssi = info.getRssi(); 272 mInfo = info; 273 mState = state; 274 refresh(); 275 } else if (mInfo != null) { 276 reorder = true; 277 mInfo = null; 278 mState = null; 279 refresh(); 280 } 281 if (reorder) { 282 notifyHierarchyChanged(); 283 } 284 } 285 getLevel()286 int getLevel() { 287 if (mRssi == Integer.MAX_VALUE) { 288 return -1; 289 } 290 return WifiManager.calculateSignalLevel(mRssi, 4); 291 } 292 getConfig()293 WifiConfiguration getConfig() { 294 return mConfig; 295 } 296 getInfo()297 WifiInfo getInfo() { 298 return mInfo; 299 } 300 getState()301 DetailedState getState() { 302 return mState; 303 } 304 removeDoubleQuotes(String string)305 static String removeDoubleQuotes(String string) { 306 int length = string.length(); 307 if ((length > 1) && (string.charAt(0) == '"') 308 && (string.charAt(length - 1) == '"')) { 309 return string.substring(1, length - 1); 310 } 311 return string; 312 } 313 convertToQuotedString(String string)314 static String convertToQuotedString(String string) { 315 return "\"" + string + "\""; 316 } 317 318 /** Updates the title and summary; may indirectly call notifyChanged() */ refresh()319 private void refresh() { 320 setTitle(ssid); 321 322 Context context = getContext(); 323 if (mState != null) { // This is the active connection 324 setSummary(Summary.get(context, mState)); 325 } else if (mRssi == Integer.MAX_VALUE) { // Wifi out of range 326 setSummary(context.getString(R.string.wifi_not_in_range)); 327 } else if (mConfig != null && mConfig.status == WifiConfiguration.Status.DISABLED) { 328 switch (mConfig.disableReason) { 329 case WifiConfiguration.DISABLED_AUTH_FAILURE: 330 setSummary(context.getString(R.string.wifi_disabled_password_failure)); 331 break; 332 case WifiConfiguration.DISABLED_DHCP_FAILURE: 333 case WifiConfiguration.DISABLED_DNS_FAILURE: 334 setSummary(context.getString(R.string.wifi_disabled_network_failure)); 335 break; 336 case WifiConfiguration.DISABLED_UNKNOWN_REASON: 337 setSummary(context.getString(R.string.wifi_disabled_generic)); 338 } 339 } else { // In range, not disabled. 340 StringBuilder summary = new StringBuilder(); 341 if (mConfig != null) { // Is saved network 342 summary.append(context.getString(R.string.wifi_remembered)); 343 } 344 345 if (security != SECURITY_NONE) { 346 String securityStrFormat; 347 if (summary.length() == 0) { 348 securityStrFormat = context.getString(R.string.wifi_secured_first_item); 349 } else { 350 securityStrFormat = context.getString(R.string.wifi_secured_second_item); 351 } 352 summary.append(String.format(securityStrFormat, getSecurityString(true))); 353 } 354 355 if (mConfig == null && wpsAvailable) { // Only list WPS available for unsaved networks 356 if (summary.length() == 0) { 357 summary.append(context.getString(R.string.wifi_wps_available_first_item)); 358 } else { 359 summary.append(context.getString(R.string.wifi_wps_available_second_item)); 360 } 361 } 362 setSummary(summary.toString()); 363 } 364 } 365 366 /** 367 * Generate and save a default wifiConfiguration with common values. 368 * Can only be called for unsecured networks. 369 * @hide 370 */ generateOpenNetworkConfig()371 protected void generateOpenNetworkConfig() { 372 if (security != SECURITY_NONE) 373 throw new IllegalStateException(); 374 if (mConfig != null) 375 return; 376 mConfig = new WifiConfiguration(); 377 mConfig.SSID = AccessPoint.convertToQuotedString(ssid); 378 mConfig.allowedKeyManagement.set(KeyMgmt.NONE); 379 } 380 } 381