WifiConfigStore.java revision 04db1d5d3a51f9b710b707cfdc1c2f41ad948237
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 android.net.wifi; 18 19import android.app.ActivityManagerNative; 20import android.content.Context; 21import android.content.Intent; 22import android.net.wifi.WifiConfiguration.Status; 23import android.text.TextUtils; 24import android.util.Log; 25 26import java.util.ArrayList; 27import java.util.BitSet; 28import java.util.List; 29 30/** 31 * This class provides the API to manage configured 32 * wifi networks. The API is not thread safe is being 33 * used only from WifiStateMachine. 34 * 35 * It deals with the following 36 * - Add/update/remove a WifiConfiguration 37 * - Maintain a list of configured networks for quick access 38 * TODO: 39 * - handle static IP per configuration 40 * - handle proxy per configuration 41 */ 42class WifiConfigStore { 43 44 private static Context sContext; 45 private static final String TAG = "WifiConfigStore"; 46 47 private static List<WifiConfiguration> sConfiguredNetworks = new ArrayList<WifiConfiguration>(); 48 /* Tracks the highest priority of configured networks */ 49 private static int sLastPriority = -1; 50 51 /** 52 * Initialize context, fetch the list of configured networks 53 * and enable all stored networks in supplicant. 54 */ 55 static void initialize(Context context) { 56 Log.d(TAG, "Updating config and enabling all networks"); 57 sContext = context; 58 updateConfiguredNetworks(); 59 enableAllNetworks(); 60 } 61 62 /** 63 * Fetch the list of currently configured networks 64 * @return List of networks 65 */ 66 static List<WifiConfiguration> getConfiguredNetworks() { 67 List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>(); 68 synchronized (sConfiguredNetworks) { 69 for (WifiConfiguration config : sConfiguredNetworks) { 70 networks.add(config.clone()); 71 } 72 } 73 return networks; 74 } 75 76 /** 77 * enable all networks and save config. This will be a no-op if the list 78 * of configured networks indicates all networks as being enabled 79 */ 80 static void enableAllNetworks() { 81 for (WifiConfiguration config : sConfiguredNetworks) { 82 if(config != null && config.status == Status.DISABLED) { 83 WifiNative.enableNetworkCommand(config.networkId, false); 84 } 85 } 86 87 WifiNative.saveConfigCommand(); 88 updateConfigAndSendChangeBroadcast(); 89 } 90 91 /** 92 * Selects the specified network config for connection. This involves 93 * addition/update of the specified config, updating the priority of 94 * all the networks and enabling the given network while disabling others. 95 * 96 * Selecting a network will leave the other networks disabled and 97 * a call to enableAllNetworks() needs to be issued upon a connection 98 * or a failure event from supplicant 99 * 100 * @param config The configuration details in WifiConfiguration 101 */ 102 static void selectNetwork(WifiConfiguration config) { 103 if (config != null) { 104 int netId = addOrUpdateNetworkNative(config); 105 selectNetwork(netId); 106 } 107 } 108 109 /** 110 * Selects the specified network for connection. This involves 111 * updating the priority of all the networks and enabling the given 112 * network while disabling others. 113 * 114 * Selecting a network will leave the other networks disabled and 115 * a call to enableAllNetworks() needs to be issued upon a connection 116 * or a failure event from supplicant 117 * 118 * @param netId network to select for connection 119 */ 120 static void selectNetwork(int netId) { 121 // Reset the priority of each network at start or if it goes too high. 122 if (sLastPriority == -1 || sLastPriority > 1000000) { 123 for (WifiConfiguration conf : sConfiguredNetworks) { 124 if (conf.networkId != -1) { 125 conf.priority = 0; 126 addOrUpdateNetworkNative(conf); 127 } 128 } 129 sLastPriority = 0; 130 } 131 132 // Set to the highest priority and save the configuration. 133 WifiConfiguration config = new WifiConfiguration(); 134 config.networkId = netId; 135 config.priority = ++sLastPriority; 136 137 addOrUpdateNetworkNative(config); 138 WifiNative.saveConfigCommand(); 139 140 /* Enable the given network while disabling all other networks */ 141 WifiNative.enableNetworkCommand(netId, true); 142 143 /* update the configured networks list but not send a 144 * broadcast to avoid a fetch from settings 145 * during this temporary disabling of networks 146 */ 147 updateConfiguredNetworks(); 148 } 149 150 /** 151 * Add/update the specified configuration and save config 152 * 153 * @param config WifiConfiguration to be saved 154 */ 155 static void saveNetwork(WifiConfiguration config) { 156 int netId = addOrUpdateNetworkNative(config); 157 /* enable a new network */ 158 if (config.networkId < 0) { 159 WifiNative.enableNetworkCommand(netId, false); 160 } 161 WifiNative.saveConfigCommand(); 162 updateConfigAndSendChangeBroadcast(); 163 } 164 165 /** 166 * Forget the specified network and save config 167 * 168 * @param netId network to forget 169 */ 170 static void forgetNetwork(int netId) { 171 WifiNative.removeNetworkCommand(netId); 172 WifiNative.saveConfigCommand(); 173 updateConfigAndSendChangeBroadcast(); 174 } 175 176 /** 177 * Add/update a network. Note that there is no saveConfig operation. 178 * This function is retained for compatibility with the public 179 * API. The more powerful saveNetwork() is used by the 180 * state machine 181 * 182 * @param config wifi configuration to add/update 183 */ 184 static int addOrUpdateNetwork(WifiConfiguration config) { 185 int ret = addOrUpdateNetworkNative(config); 186 updateConfigAndSendChangeBroadcast(); 187 return ret; 188 } 189 190 /** 191 * Remove a network. Note that there is no saveConfig operation. 192 * This function is retained for compatibility with the public 193 * API. The more powerful forgetNetwork() is used by the 194 * state machine for network removal 195 * 196 * @param netId network to be removed 197 */ 198 static boolean removeNetwork(int netId) { 199 boolean ret = WifiNative.removeNetworkCommand(netId); 200 updateConfigAndSendChangeBroadcast(); 201 return ret; 202 } 203 204 /** 205 * Enable a network. Note that there is no saveConfig operation. 206 * This function is retained for compatibility with the public 207 * API. The more powerful selectNetwork()/saveNetwork() is used by the 208 * state machine for connecting to a network 209 * 210 * @param netId network to be removed 211 */ 212 static boolean enableNetwork(int netId, boolean disableOthers) { 213 boolean ret = WifiNative.enableNetworkCommand(netId, disableOthers); 214 updateConfigAndSendChangeBroadcast(); 215 return ret; 216 } 217 218 /** 219 * Disable a network. Note that there is no saveConfig operation. 220 * @param netId network to be disabled 221 */ 222 static boolean disableNetwork(int netId) { 223 boolean ret = WifiNative.disableNetworkCommand(netId); 224 updateConfigAndSendChangeBroadcast(); 225 return ret; 226 } 227 228 /** 229 * Save the configured networks in supplicant to disk 230 */ 231 static boolean saveConfig() { 232 return WifiNative.saveConfigCommand(); 233 } 234 235 private static void updateConfigAndSendChangeBroadcast() { 236 updateConfiguredNetworks(); 237 if (!ActivityManagerNative.isSystemReady()) return; 238 Intent intent = new Intent(WifiManager.SUPPLICANT_CONFIG_CHANGED_ACTION); 239 sContext.sendBroadcast(intent); 240 } 241 242 private static void updateConfiguredNetworks() { 243 String listStr = WifiNative.listNetworksCommand(); 244 sLastPriority = 0; 245 246 synchronized (sConfiguredNetworks) { 247 sConfiguredNetworks.clear(); 248 249 if (listStr == null) 250 return; 251 252 String[] lines = listStr.split("\n"); 253 // Skip the first line, which is a header 254 for (int i = 1; i < lines.length; i++) { 255 String[] result = lines[i].split("\t"); 256 // network-id | ssid | bssid | flags 257 WifiConfiguration config = new WifiConfiguration(); 258 try { 259 config.networkId = Integer.parseInt(result[0]); 260 } catch(NumberFormatException e) { 261 continue; 262 } 263 if (result.length > 3) { 264 if (result[3].indexOf("[CURRENT]") != -1) 265 config.status = WifiConfiguration.Status.CURRENT; 266 else if (result[3].indexOf("[DISABLED]") != -1) 267 config.status = WifiConfiguration.Status.DISABLED; 268 else 269 config.status = WifiConfiguration.Status.ENABLED; 270 } else { 271 config.status = WifiConfiguration.Status.ENABLED; 272 } 273 readNetworkVariables(config); 274 if (config.priority > sLastPriority) { 275 sLastPriority = config.priority; 276 } 277 sConfiguredNetworks.add(config); 278 } 279 } 280 } 281 282 private static int addOrUpdateNetworkNative(WifiConfiguration config) { 283 /* 284 * If the supplied networkId is -1, we create a new empty 285 * network configuration. Otherwise, the networkId should 286 * refer to an existing configuration. 287 */ 288 int netId = config.networkId; 289 boolean newNetwork = netId == -1; 290 // networkId of -1 means we want to create a new network 291 292 if (newNetwork) { 293 netId = WifiNative.addNetworkCommand(); 294 if (netId < 0) { 295 Log.e(TAG, "Failed to add a network!"); 296 return -1; 297 } 298 } 299 300 setVariables: { 301 302 if (config.SSID != null && 303 !WifiNative.setNetworkVariableCommand( 304 netId, 305 WifiConfiguration.ssidVarName, 306 config.SSID)) { 307 Log.d(TAG, "failed to set SSID: "+config.SSID); 308 break setVariables; 309 } 310 311 if (config.BSSID != null && 312 !WifiNative.setNetworkVariableCommand( 313 netId, 314 WifiConfiguration.bssidVarName, 315 config.BSSID)) { 316 Log.d(TAG, "failed to set BSSID: "+config.BSSID); 317 break setVariables; 318 } 319 320 String allowedKeyManagementString = 321 makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings); 322 if (config.allowedKeyManagement.cardinality() != 0 && 323 !WifiNative.setNetworkVariableCommand( 324 netId, 325 WifiConfiguration.KeyMgmt.varName, 326 allowedKeyManagementString)) { 327 Log.d(TAG, "failed to set key_mgmt: "+ 328 allowedKeyManagementString); 329 break setVariables; 330 } 331 332 String allowedProtocolsString = 333 makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings); 334 if (config.allowedProtocols.cardinality() != 0 && 335 !WifiNative.setNetworkVariableCommand( 336 netId, 337 WifiConfiguration.Protocol.varName, 338 allowedProtocolsString)) { 339 Log.d(TAG, "failed to set proto: "+ 340 allowedProtocolsString); 341 break setVariables; 342 } 343 344 String allowedAuthAlgorithmsString = 345 makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings); 346 if (config.allowedAuthAlgorithms.cardinality() != 0 && 347 !WifiNative.setNetworkVariableCommand( 348 netId, 349 WifiConfiguration.AuthAlgorithm.varName, 350 allowedAuthAlgorithmsString)) { 351 Log.d(TAG, "failed to set auth_alg: "+ 352 allowedAuthAlgorithmsString); 353 break setVariables; 354 } 355 356 String allowedPairwiseCiphersString = 357 makeString(config.allowedPairwiseCiphers, 358 WifiConfiguration.PairwiseCipher.strings); 359 if (config.allowedPairwiseCiphers.cardinality() != 0 && 360 !WifiNative.setNetworkVariableCommand( 361 netId, 362 WifiConfiguration.PairwiseCipher.varName, 363 allowedPairwiseCiphersString)) { 364 Log.d(TAG, "failed to set pairwise: "+ 365 allowedPairwiseCiphersString); 366 break setVariables; 367 } 368 369 String allowedGroupCiphersString = 370 makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings); 371 if (config.allowedGroupCiphers.cardinality() != 0 && 372 !WifiNative.setNetworkVariableCommand( 373 netId, 374 WifiConfiguration.GroupCipher.varName, 375 allowedGroupCiphersString)) { 376 Log.d(TAG, "failed to set group: "+ 377 allowedGroupCiphersString); 378 break setVariables; 379 } 380 381 // Prevent client screw-up by passing in a WifiConfiguration we gave it 382 // by preventing "*" as a key. 383 if (config.preSharedKey != null && !config.preSharedKey.equals("*") && 384 !WifiNative.setNetworkVariableCommand( 385 netId, 386 WifiConfiguration.pskVarName, 387 config.preSharedKey)) { 388 Log.d(TAG, "failed to set psk: "+config.preSharedKey); 389 break setVariables; 390 } 391 392 boolean hasSetKey = false; 393 if (config.wepKeys != null) { 394 for (int i = 0; i < config.wepKeys.length; i++) { 395 // Prevent client screw-up by passing in a WifiConfiguration we gave it 396 // by preventing "*" as a key. 397 if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) { 398 if (!WifiNative.setNetworkVariableCommand( 399 netId, 400 WifiConfiguration.wepKeyVarNames[i], 401 config.wepKeys[i])) { 402 Log.d(TAG, 403 "failed to set wep_key"+i+": " + 404 config.wepKeys[i]); 405 break setVariables; 406 } 407 hasSetKey = true; 408 } 409 } 410 } 411 412 if (hasSetKey) { 413 if (!WifiNative.setNetworkVariableCommand( 414 netId, 415 WifiConfiguration.wepTxKeyIdxVarName, 416 Integer.toString(config.wepTxKeyIndex))) { 417 Log.d(TAG, 418 "failed to set wep_tx_keyidx: "+ 419 config.wepTxKeyIndex); 420 break setVariables; 421 } 422 } 423 424 if (!WifiNative.setNetworkVariableCommand( 425 netId, 426 WifiConfiguration.priorityVarName, 427 Integer.toString(config.priority))) { 428 Log.d(TAG, config.SSID + ": failed to set priority: " 429 +config.priority); 430 break setVariables; 431 } 432 433 if (config.hiddenSSID && !WifiNative.setNetworkVariableCommand( 434 netId, 435 WifiConfiguration.hiddenSSIDVarName, 436 Integer.toString(config.hiddenSSID ? 1 : 0))) { 437 Log.d(TAG, config.SSID + ": failed to set hiddenSSID: "+ 438 config.hiddenSSID); 439 break setVariables; 440 } 441 442 for (WifiConfiguration.EnterpriseField field 443 : config.enterpriseFields) { 444 String varName = field.varName(); 445 String value = field.value(); 446 if (value != null) { 447 if (field != config.eap) { 448 value = (value.length() == 0) ? "NULL" : convertToQuotedString(value); 449 } 450 if (!WifiNative.setNetworkVariableCommand( 451 netId, 452 varName, 453 value)) { 454 Log.d(TAG, config.SSID + ": failed to set " + varName + 455 ": " + value); 456 break setVariables; 457 } 458 } 459 } 460 return netId; 461 } 462 463 if (newNetwork) { 464 WifiNative.removeNetworkCommand(netId); 465 Log.d(TAG, 466 "Failed to set a network variable, removed network: " 467 + netId); 468 } 469 470 return -1; 471 } 472 473 /** 474 * Read the variables from the supplicant daemon that are needed to 475 * fill in the WifiConfiguration object. 476 * 477 * @param config the {@link WifiConfiguration} object to be filled in. 478 */ 479 private static void readNetworkVariables(WifiConfiguration config) { 480 481 int netId = config.networkId; 482 if (netId < 0) 483 return; 484 485 /* 486 * TODO: maybe should have a native method that takes an array of 487 * variable names and returns an array of values. But we'd still 488 * be doing a round trip to the supplicant daemon for each variable. 489 */ 490 String value; 491 492 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.ssidVarName); 493 if (!TextUtils.isEmpty(value)) { 494 config.SSID = removeDoubleQuotes(value); 495 } else { 496 config.SSID = null; 497 } 498 499 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.bssidVarName); 500 if (!TextUtils.isEmpty(value)) { 501 config.BSSID = value; 502 } else { 503 config.BSSID = null; 504 } 505 506 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.priorityVarName); 507 config.priority = -1; 508 if (!TextUtils.isEmpty(value)) { 509 try { 510 config.priority = Integer.parseInt(value); 511 } catch (NumberFormatException ignore) { 512 } 513 } 514 515 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.hiddenSSIDVarName); 516 config.hiddenSSID = false; 517 if (!TextUtils.isEmpty(value)) { 518 try { 519 config.hiddenSSID = Integer.parseInt(value) != 0; 520 } catch (NumberFormatException ignore) { 521 } 522 } 523 524 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.wepTxKeyIdxVarName); 525 config.wepTxKeyIndex = -1; 526 if (!TextUtils.isEmpty(value)) { 527 try { 528 config.wepTxKeyIndex = Integer.parseInt(value); 529 } catch (NumberFormatException ignore) { 530 } 531 } 532 533 for (int i = 0; i < 4; i++) { 534 value = WifiNative.getNetworkVariableCommand(netId, 535 WifiConfiguration.wepKeyVarNames[i]); 536 if (!TextUtils.isEmpty(value)) { 537 config.wepKeys[i] = value; 538 } else { 539 config.wepKeys[i] = null; 540 } 541 } 542 543 value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.pskVarName); 544 if (!TextUtils.isEmpty(value)) { 545 config.preSharedKey = value; 546 } else { 547 config.preSharedKey = null; 548 } 549 550 value = WifiNative.getNetworkVariableCommand(config.networkId, 551 WifiConfiguration.Protocol.varName); 552 if (!TextUtils.isEmpty(value)) { 553 String vals[] = value.split(" "); 554 for (String val : vals) { 555 int index = 556 lookupString(val, WifiConfiguration.Protocol.strings); 557 if (0 <= index) { 558 config.allowedProtocols.set(index); 559 } 560 } 561 } 562 563 value = WifiNative.getNetworkVariableCommand(config.networkId, 564 WifiConfiguration.KeyMgmt.varName); 565 if (!TextUtils.isEmpty(value)) { 566 String vals[] = value.split(" "); 567 for (String val : vals) { 568 int index = 569 lookupString(val, WifiConfiguration.KeyMgmt.strings); 570 if (0 <= index) { 571 config.allowedKeyManagement.set(index); 572 } 573 } 574 } 575 576 value = WifiNative.getNetworkVariableCommand(config.networkId, 577 WifiConfiguration.AuthAlgorithm.varName); 578 if (!TextUtils.isEmpty(value)) { 579 String vals[] = value.split(" "); 580 for (String val : vals) { 581 int index = 582 lookupString(val, WifiConfiguration.AuthAlgorithm.strings); 583 if (0 <= index) { 584 config.allowedAuthAlgorithms.set(index); 585 } 586 } 587 } 588 589 value = WifiNative.getNetworkVariableCommand(config.networkId, 590 WifiConfiguration.PairwiseCipher.varName); 591 if (!TextUtils.isEmpty(value)) { 592 String vals[] = value.split(" "); 593 for (String val : vals) { 594 int index = 595 lookupString(val, WifiConfiguration.PairwiseCipher.strings); 596 if (0 <= index) { 597 config.allowedPairwiseCiphers.set(index); 598 } 599 } 600 } 601 602 value = WifiNative.getNetworkVariableCommand(config.networkId, 603 WifiConfiguration.GroupCipher.varName); 604 if (!TextUtils.isEmpty(value)) { 605 String vals[] = value.split(" "); 606 for (String val : vals) { 607 int index = 608 lookupString(val, WifiConfiguration.GroupCipher.strings); 609 if (0 <= index) { 610 config.allowedGroupCiphers.set(index); 611 } 612 } 613 } 614 615 for (WifiConfiguration.EnterpriseField field : 616 config.enterpriseFields) { 617 value = WifiNative.getNetworkVariableCommand(netId, 618 field.varName()); 619 if (!TextUtils.isEmpty(value)) { 620 if (field != config.eap) value = removeDoubleQuotes(value); 621 field.setValue(value); 622 } 623 } 624 } 625 626 private static String removeDoubleQuotes(String string) { 627 if (string.length() <= 2) return ""; 628 return string.substring(1, string.length() - 1); 629 } 630 631 private static String convertToQuotedString(String string) { 632 return "\"" + string + "\""; 633 } 634 635 private static String makeString(BitSet set, String[] strings) { 636 StringBuffer buf = new StringBuffer(); 637 int nextSetBit = -1; 638 639 /* Make sure all set bits are in [0, strings.length) to avoid 640 * going out of bounds on strings. (Shouldn't happen, but...) */ 641 set = set.get(0, strings.length); 642 643 while ((nextSetBit = set.nextSetBit(nextSetBit + 1)) != -1) { 644 buf.append(strings[nextSetBit].replace('_', '-')).append(' '); 645 } 646 647 // remove trailing space 648 if (set.cardinality() > 0) { 649 buf.setLength(buf.length() - 1); 650 } 651 652 return buf.toString(); 653 } 654 655 private static int lookupString(String string, String[] strings) { 656 int size = strings.length; 657 658 string = string.replace('-', '_'); 659 660 for (int i = 0; i < size; i++) 661 if (string.equals(strings[i])) 662 return i; 663 664 // if we ever get here, we should probably add the 665 // value to WifiConfiguration to reflect that it's 666 // supported by the WPA supplicant 667 Log.w(TAG, "Failed to look-up a string: " + string); 668 669 return -1; 670 } 671 672 static String dump() { 673 StringBuffer sb = new StringBuffer(); 674 String LS = System.getProperty("line.separator"); 675 sb.append("sLastPriority ").append(sLastPriority).append(LS); 676 sb.append("Configured networks ").append(LS); 677 for (WifiConfiguration conf : getConfiguredNetworks()) { 678 sb.append(conf).append(LS); 679 } 680 return sb.toString(); 681 } 682} 683