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 17package com.android.settings.wifi; 18 19import android.content.Context; 20import android.net.NetworkInfo.DetailedState; 21import android.net.wifi.ScanResult; 22import android.net.wifi.WifiConfiguration; 23import android.net.wifi.WifiConfiguration.KeyMgmt; 24import android.net.wifi.WifiInfo; 25import android.net.wifi.WifiManager; 26import android.os.Bundle; 27import android.preference.Preference; 28import android.util.Log; 29import android.view.View; 30import android.widget.ImageView; 31 32import com.android.settings.R; 33 34class 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 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 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 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 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 143 AccessPoint(Context context, WifiConfiguration config) { 144 super(context); 145 setWidgetLayoutResource(R.layout.preference_widget_wifi_signal); 146 loadConfig(config); 147 refresh(); 148 } 149 150 AccessPoint(Context context, ScanResult result) { 151 super(context); 152 setWidgetLayoutResource(R.layout.preference_widget_wifi_signal); 153 loadResult(result); 154 refresh(); 155 } 156 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 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 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 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 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 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 != null && other.mInfo == null) return -1; 228 if (mInfo == null && other.mInfo != null) return 1; 229 230 // Reachable one goes before unreachable one. 231 if (mRssi != Integer.MAX_VALUE && other.mRssi == Integer.MAX_VALUE) return -1; 232 if (mRssi == Integer.MAX_VALUE && other.mRssi != Integer.MAX_VALUE) return 1; 233 234 // Configured one goes before unconfigured one. 235 if (networkId != WifiConfiguration.INVALID_NETWORK_ID 236 && other.networkId == WifiConfiguration.INVALID_NETWORK_ID) return -1; 237 if (networkId == WifiConfiguration.INVALID_NETWORK_ID 238 && other.networkId != WifiConfiguration.INVALID_NETWORK_ID) return 1; 239 240 // Sort by signal strength. 241 int difference = WifiManager.compareSignalLevel(other.mRssi, mRssi); 242 if (difference != 0) { 243 return difference; 244 } 245 // Sort by ssid. 246 return ssid.compareToIgnoreCase(other.ssid); 247 } 248 249 @Override 250 public boolean equals(Object other) { 251 if (!(other instanceof AccessPoint)) return false; 252 return (this.compareTo((AccessPoint) other) == 0); 253 } 254 255 @Override 256 public int hashCode() { 257 int result = 0; 258 if (mInfo != null) result += 13 * mInfo.hashCode(); 259 result += 19 * mRssi; 260 result += 23 * networkId; 261 result += 29 * ssid.hashCode(); 262 return result; 263 } 264 265 boolean update(ScanResult result) { 266 if (ssid.equals(result.SSID) && security == getSecurity(result)) { 267 if (WifiManager.compareSignalLevel(result.level, mRssi) > 0) { 268 int oldLevel = getLevel(); 269 mRssi = result.level; 270 if (getLevel() != oldLevel) { 271 notifyChanged(); 272 } 273 } 274 // This flag only comes from scans, is not easily saved in config 275 if (security == SECURITY_PSK) { 276 pskType = getPskType(result); 277 } 278 refresh(); 279 return true; 280 } 281 return false; 282 } 283 284 void update(WifiInfo info, DetailedState state) { 285 boolean reorder = false; 286 if (info != null && networkId != WifiConfiguration.INVALID_NETWORK_ID 287 && networkId == info.getNetworkId()) { 288 reorder = (mInfo == null); 289 mRssi = info.getRssi(); 290 mInfo = info; 291 mState = state; 292 refresh(); 293 } else if (mInfo != null) { 294 reorder = true; 295 mInfo = null; 296 mState = null; 297 refresh(); 298 } 299 if (reorder) { 300 notifyHierarchyChanged(); 301 } 302 } 303 304 int getLevel() { 305 if (mRssi == Integer.MAX_VALUE) { 306 return -1; 307 } 308 return WifiManager.calculateSignalLevel(mRssi, 4); 309 } 310 311 WifiConfiguration getConfig() { 312 return mConfig; 313 } 314 315 WifiInfo getInfo() { 316 return mInfo; 317 } 318 319 DetailedState getState() { 320 return mState; 321 } 322 323 static String removeDoubleQuotes(String string) { 324 int length = string.length(); 325 if ((length > 1) && (string.charAt(0) == '"') 326 && (string.charAt(length - 1) == '"')) { 327 return string.substring(1, length - 1); 328 } 329 return string; 330 } 331 332 static String convertToQuotedString(String string) { 333 return "\"" + string + "\""; 334 } 335 336 /** Updates the title and summary; may indirectly call notifyChanged() */ 337 private void refresh() { 338 setTitle(ssid); 339 340 Context context = getContext(); 341 if (mConfig != null && mConfig.status == WifiConfiguration.Status.DISABLED) { 342 switch (mConfig.disableReason) { 343 case WifiConfiguration.DISABLED_AUTH_FAILURE: 344 setSummary(context.getString(R.string.wifi_disabled_password_failure)); 345 break; 346 case WifiConfiguration.DISABLED_DHCP_FAILURE: 347 case WifiConfiguration.DISABLED_DNS_FAILURE: 348 setSummary(context.getString(R.string.wifi_disabled_network_failure)); 349 break; 350 case WifiConfiguration.DISABLED_UNKNOWN_REASON: 351 setSummary(context.getString(R.string.wifi_disabled_generic)); 352 } 353 } else if (mRssi == Integer.MAX_VALUE) { // Wifi out of range 354 setSummary(context.getString(R.string.wifi_not_in_range)); 355 } else if (mState != null) { // This is the active connection 356 setSummary(Summary.get(context, mState)); 357 } else { // In range, not disabled. 358 StringBuilder summary = new StringBuilder(); 359 if (mConfig != null) { // Is saved network 360 summary.append(context.getString(R.string.wifi_remembered)); 361 } 362 363 if (security != SECURITY_NONE) { 364 String securityStrFormat; 365 if (summary.length() == 0) { 366 securityStrFormat = context.getString(R.string.wifi_secured_first_item); 367 } else { 368 securityStrFormat = context.getString(R.string.wifi_secured_second_item); 369 } 370 summary.append(String.format(securityStrFormat, getSecurityString(true))); 371 } 372 373 if (mConfig == null && wpsAvailable) { // Only list WPS available for unsaved networks 374 if (summary.length() == 0) { 375 summary.append(context.getString(R.string.wifi_wps_available_first_item)); 376 } else { 377 summary.append(context.getString(R.string.wifi_wps_available_second_item)); 378 } 379 } 380 setSummary(summary.toString()); 381 } 382 } 383 384 /** 385 * Generate and save a default wifiConfiguration with common values. 386 * Can only be called for unsecured networks. 387 * @hide 388 */ 389 protected void generateOpenNetworkConfig() { 390 if (security != SECURITY_NONE) 391 throw new IllegalStateException(); 392 if (mConfig != null) 393 return; 394 mConfig = new WifiConfiguration(); 395 mConfig.SSID = AccessPoint.convertToQuotedString(ssid); 396 mConfig.allowedKeyManagement.set(KeyMgmt.NONE); 397 } 398} 399