WifiNative.java revision 1f095869536472c178046bb63c59947508eac4a6
1/* 2 * Copyright (C) 2008 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.net.wifi.p2p.WifiP2pConfig; 20import android.net.wifi.p2p.WifiP2pGroup; 21import android.net.wifi.p2p.WifiP2pDevice; 22import android.text.TextUtils; 23import android.util.Log; 24 25import java.io.InputStream; 26import java.lang.Process; 27import java.util.ArrayList; 28import java.util.List; 29 30/** 31 * Native calls for bring up/shut down of the supplicant daemon and for 32 * sending requests to the supplicant daemon 33 * 34 * waitForEvent() is called on the monitor thread for events. All other methods 35 * must be serialized from the framework. 36 * 37 * {@hide} 38 */ 39public class WifiNative { 40 41 static final int BLUETOOTH_COEXISTENCE_MODE_ENABLED = 0; 42 static final int BLUETOOTH_COEXISTENCE_MODE_DISABLED = 1; 43 static final int BLUETOOTH_COEXISTENCE_MODE_SENSE = 2; 44 45 public native static boolean loadDriver(); 46 47 public native static boolean isDriverLoaded(); 48 49 public native static boolean unloadDriver(); 50 51 public native static boolean startSupplicant(); 52 53 public native static boolean startP2pSupplicant(); 54 55 /* Sends a kill signal to supplicant. To be used when we have lost connection 56 or when the supplicant is hung */ 57 public native static boolean killSupplicant(); 58 59 public native static boolean connectToSupplicant(); 60 61 public native static void closeSupplicantConnection(); 62 63 /** 64 * Wait for the supplicant to send an event, returning the event string. 65 * @return the event string sent by the supplicant. 66 */ 67 public native static String waitForEvent(); 68 69 private native static boolean doBooleanCommand(String command); 70 71 private native static int doIntCommand(String command); 72 73 private native static String doStringCommand(String command); 74 75 public static boolean ping() { 76 String pong = doStringCommand("PING"); 77 return (pong != null && pong.equals("PONG")); 78 } 79 80 public static boolean scan() { 81 return doBooleanCommand("SCAN"); 82 } 83 84 public static boolean setScanMode(boolean setActive) { 85 if (setActive) { 86 return doBooleanCommand("DRIVER SCAN-ACTIVE"); 87 } else { 88 return doBooleanCommand("DRIVER SCAN-PASSIVE"); 89 } 90 } 91 92 /* Does a graceful shutdown of supplicant. Is a common stop function for both p2p and sta. 93 * 94 * Note that underneath we use a harsh-sounding "terminate" supplicant command 95 * for a graceful stop and a mild-sounding "stop" interface 96 * to kill the process 97 */ 98 public static boolean stopSupplicant() { 99 return doBooleanCommand("TERMINATE"); 100 } 101 102 public static String listNetworks() { 103 return doStringCommand("LIST_NETWORKS"); 104 } 105 106 public static int addNetwork() { 107 return doIntCommand("ADD_NETWORK"); 108 } 109 110 public static boolean setNetworkVariable(int netId, String name, String value) { 111 if (TextUtils.isEmpty(name) || TextUtils.isEmpty(value)) return false; 112 return doBooleanCommand("SET_NETWORK " + netId + " " + name + " " + value); 113 } 114 115 public static String getNetworkVariable(int netId, String name) { 116 if (TextUtils.isEmpty(name)) return null; 117 return doStringCommand("GET_NETWORK " + netId + " " + name); 118 } 119 120 public static boolean removeNetwork(int netId) { 121 return doBooleanCommand("REMOVE_NETWORK " + netId); 122 } 123 124 public static boolean enableNetwork(int netId, boolean disableOthers) { 125 if (disableOthers) { 126 return doBooleanCommand("SELECT_NETWORK " + netId); 127 } else { 128 return doBooleanCommand("ENABLE_NETWORK " + netId); 129 } 130 } 131 132 public static boolean disableNetwork(int netId) { 133 return doBooleanCommand("DISABLE_NETWORK " + netId); 134 } 135 136 public static boolean reconnect() { 137 return doBooleanCommand("RECONNECT"); 138 } 139 140 public static boolean reassociate() { 141 return doBooleanCommand("REASSOCIATE"); 142 } 143 144 public static boolean disconnect() { 145 return doBooleanCommand("DISCONNECT"); 146 } 147 148 public static String status() { 149 return doStringCommand("STATUS"); 150 } 151 152 public static String getMacAddress() { 153 //Macaddr = XX.XX.XX.XX.XX.XX 154 String ret = doStringCommand("DRIVER MACADDR"); 155 if (!TextUtils.isEmpty(ret)) { 156 String[] tokens = ret.split(" = "); 157 if (tokens.length == 2) return tokens[1]; 158 } 159 return null; 160 } 161 162 public static String scanResults() { 163 return doStringCommand("SCAN_RESULTS"); 164 } 165 166 public static boolean startDriver() { 167 return doBooleanCommand("DRIVER START"); 168 } 169 170 public static boolean stopDriver() { 171 return doBooleanCommand("DRIVER STOP"); 172 } 173 174 175 /** 176 * Start filtering out Multicast V4 packets 177 * @return {@code true} if the operation succeeded, {@code false} otherwise 178 * 179 * Multicast filtering rules work as follows: 180 * 181 * The driver can filter multicast (v4 and/or v6) and broadcast packets when in 182 * a power optimized mode (typically when screen goes off). 183 * 184 * In order to prevent the driver from filtering the multicast/broadcast packets, we have to 185 * add a DRIVER RXFILTER-ADD rule followed by DRIVER RXFILTER-START to make the rule effective 186 * 187 * DRIVER RXFILTER-ADD Num 188 * where Num = 0 - Unicast, 1 - Broadcast, 2 - Mutil4 or 3 - Multi6 189 * 190 * and DRIVER RXFILTER-START 191 * In order to stop the usage of these rules, we do 192 * 193 * DRIVER RXFILTER-STOP 194 * DRIVER RXFILTER-REMOVE Num 195 * where Num is as described for RXFILTER-ADD 196 * 197 * The SETSUSPENDOPT driver command overrides the filtering rules 198 */ 199 public static boolean startFilteringMulticastV4Packets() { 200 return doBooleanCommand("DRIVER RXFILTER-STOP") 201 && doBooleanCommand("DRIVER RXFILTER-REMOVE 2") 202 && doBooleanCommand("DRIVER RXFILTER-START"); 203 } 204 205 /** 206 * Stop filtering out Multicast V4 packets. 207 * @return {@code true} if the operation succeeded, {@code false} otherwise 208 */ 209 public static boolean stopFilteringMulticastV4Packets() { 210 return doBooleanCommand("DRIVER RXFILTER-STOP") 211 && doBooleanCommand("DRIVER RXFILTER-ADD 2") 212 && doBooleanCommand("DRIVER RXFILTER-START"); 213 } 214 215 /** 216 * Start filtering out Multicast V6 packets 217 * @return {@code true} if the operation succeeded, {@code false} otherwise 218 */ 219 public static boolean startFilteringMulticastV6Packets() { 220 return doBooleanCommand("DRIVER RXFILTER-STOP") 221 && doBooleanCommand("DRIVER RXFILTER-REMOVE 3") 222 && doBooleanCommand("DRIVER RXFILTER-START"); 223 } 224 225 /** 226 * Stop filtering out Multicast V6 packets. 227 * @return {@code true} if the operation succeeded, {@code false} otherwise 228 */ 229 public static boolean stopFilteringMulticastV6Packets() { 230 return doBooleanCommand("DRIVER RXFILTER-STOP") 231 && doBooleanCommand("DRIVER RXFILTER-ADD 3") 232 && doBooleanCommand("DRIVER RXFILTER-START"); 233 } 234 235 public static int getPowerMode() { 236 String ret = doStringCommand("DRIVER GETPOWER"); 237 if (!TextUtils.isEmpty(ret)) { 238 // reply comes back in the form "powermode = XX" where XX is the 239 // number we're interested in. 240 String[] tokens = ret.split(" = "); 241 try { 242 if (tokens.length == 2) return Integer.parseInt(tokens[1]); 243 } catch (NumberFormatException e) { 244 return -1; 245 } 246 } 247 return -1; 248 } 249 250 public static boolean setPowerMode(int mode) { 251 return doBooleanCommand("DRIVER POWERMODE " + mode); 252 } 253 254 public static int getBand() { 255 String ret = doStringCommand("DRIVER GETBAND"); 256 if (!TextUtils.isEmpty(ret)) { 257 //reply is "BAND X" where X is the band 258 String[] tokens = ret.split(" "); 259 try { 260 if (tokens.length == 2) return Integer.parseInt(tokens[1]); 261 } catch (NumberFormatException e) { 262 return -1; 263 } 264 } 265 return -1; 266 } 267 268 public static boolean setBand(int band) { 269 return doBooleanCommand("DRIVER SETBAND " + band); 270 } 271 272 /** 273 * Sets the bluetooth coexistence mode. 274 * 275 * @param mode One of {@link #BLUETOOTH_COEXISTENCE_MODE_DISABLED}, 276 * {@link #BLUETOOTH_COEXISTENCE_MODE_ENABLED}, or 277 * {@link #BLUETOOTH_COEXISTENCE_MODE_SENSE}. 278 * @return Whether the mode was successfully set. 279 */ 280 public static boolean setBluetoothCoexistenceMode(int mode) { 281 return doBooleanCommand("DRIVER BTCOEXMODE " + mode); 282 } 283 284 /** 285 * Enable or disable Bluetooth coexistence scan mode. When this mode is on, 286 * some of the low-level scan parameters used by the driver are changed to 287 * reduce interference with A2DP streaming. 288 * 289 * @param isSet whether to enable or disable this mode 290 * @return {@code true} if the command succeeded, {@code false} otherwise. 291 */ 292 public static boolean setBluetoothCoexistenceScanMode(boolean setCoexScanMode) { 293 if (setCoexScanMode) { 294 return doBooleanCommand("DRIVER BTCOEXSCAN-START"); 295 } else { 296 return doBooleanCommand("DRIVER BTCOEXSCAN-STOP"); 297 } 298 } 299 300 public static boolean saveConfig() { 301 // Make sure we never write out a value for AP_SCAN other than 1 302 return doBooleanCommand("AP_SCAN 1") && doBooleanCommand("SAVE_CONFIG"); 303 } 304 305 public static boolean setScanResultHandling(int mode) { 306 return doBooleanCommand("AP_SCAN " + mode); 307 } 308 309 public static boolean addToBlacklist(String bssid) { 310 if (TextUtils.isEmpty(bssid)) return false; 311 return doBooleanCommand("BLACKLIST " + bssid); 312 } 313 314 public static boolean clearBlacklist() { 315 return doBooleanCommand("BLACKLIST clear"); 316 } 317 318 public static boolean setSuspendOptimizations(boolean enabled) { 319 if (enabled) { 320 return doBooleanCommand("DRIVER SETSUSPENDOPT 0"); 321 } else { 322 return doBooleanCommand("DRIVER SETSUSPENDOPT 1"); 323 } 324 } 325 326 public static boolean setCountryCode(String countryCode) { 327 return doBooleanCommand("DRIVER COUNTRY " + countryCode); 328 } 329 330 public static void enableBackgroundScan(boolean enable) { 331 //Note: BGSCAN-START and BGSCAN-STOP are documented in core/res/res/values/config.xml 332 //and will need an update if the names are changed 333 if (enable) { 334 doBooleanCommand("DRIVER BGSCAN-START"); 335 } else { 336 doBooleanCommand("DRIVER BGSCAN-STOP"); 337 } 338 } 339 340 public static void setScanInterval(int scanInterval) { 341 doBooleanCommand("SCAN_INTERVAL " + scanInterval); 342 } 343 344 /** Example output: 345 * RSSI=-65 346 * LINKSPEED=48 347 * NOISE=9999 348 * FREQUENCY=0 349 */ 350 public static String signalPoll() { 351 return doStringCommand("SIGNAL_POLL"); 352 } 353 354 public static boolean startWpsPbc() { 355 return doBooleanCommand("WPS_PBC"); 356 } 357 358 public static boolean startWpsPbc(String bssid) { 359 return doBooleanCommand("WPS_PBC " + bssid); 360 } 361 362 public static boolean startWpsPinKeypad(String pin) { 363 return doBooleanCommand("WPS_PIN any " + pin); 364 } 365 366 public static String startWpsPinDisplay(String bssid) { 367 return doStringCommand("WPS_PIN " + bssid); 368 } 369 370 /* Configures an access point connection */ 371 public static boolean startWpsRegistrar(String bssid, String pin) { 372 return doBooleanCommand("WPS_REG " + bssid + " " + pin); 373 } 374 375 public static boolean setPersistentReconnect(boolean enabled) { 376 int value = (enabled == true) ? 1 : 0; 377 return doBooleanCommand("SET persistent_reconnect " + value); 378 } 379 380 public static boolean setDeviceName(String name) { 381 return doBooleanCommand("SET device_name " + name); 382 } 383 384 public static boolean setDeviceType(String type) { 385 return doBooleanCommand("SET device_type " + type); 386 } 387 388 public static boolean setConfigMethods(String cfg) { 389 return doBooleanCommand("SET config_methods " + cfg); 390 } 391 392 public static boolean setP2pSsidPostfix(String postfix) { 393 return doBooleanCommand("SET p2p_ssid_postfix " + postfix); 394 } 395 396 public static boolean p2pFind() { 397 return doBooleanCommand("P2P_FIND"); 398 } 399 400 public static boolean p2pFind(int timeout) { 401 if (timeout <= 0) { 402 return p2pFind(); 403 } 404 return doBooleanCommand("P2P_FIND " + timeout); 405 } 406 407 public static boolean p2pListen() { 408 return doBooleanCommand("P2P_LISTEN"); 409 } 410 411 public static boolean p2pListen(int timeout) { 412 if (timeout <= 0) { 413 return p2pListen(); 414 } 415 return doBooleanCommand("P2P_LISTEN " + timeout); 416 } 417 418 public static boolean p2pFlush() { 419 return doBooleanCommand("P2P_FLUSH"); 420 } 421 422 /* p2p_connect <peer device address> <pbc|pin|PIN#> [label|display|keypad] 423 [persistent] [join|auth] [go_intent=<0..15>] [freq=<in MHz>] */ 424 public static String p2pConnect(WifiP2pConfig config, boolean joinExistingGroup) { 425 if (config == null) return null; 426 List<String> args = new ArrayList<String>(); 427 WpsInfo wps = config.wps; 428 args.add(config.deviceAddress); 429 430 switch (wps.setup) { 431 case WpsInfo.PBC: 432 args.add("pbc"); 433 break; 434 case WpsInfo.DISPLAY: 435 if (TextUtils.isEmpty(wps.pin)) { 436 args.add("pin"); 437 } else { 438 args.add(wps.pin); 439 } 440 args.add("display"); 441 break; 442 case WpsInfo.KEYPAD: 443 args.add(wps.pin); 444 args.add("keypad"); 445 break; 446 case WpsInfo.LABEL: 447 args.add(wps.pin); 448 args.add("label"); 449 default: 450 break; 451 } 452 453 //TODO: Add persist behavior once the supplicant interaction is fixed for both 454 // group and client scenarios 455 /* Persist unless there is an explicit request to not do so*/ 456 //if (config.persist != WifiP2pConfig.Persist.NO) args.add("persistent"); 457 458 if (joinExistingGroup) args.add("join"); 459 460 //TODO: This can be adapted based on device plugged in state and 461 //device battery state 462 int groupOwnerIntent = config.groupOwnerIntent; 463 if (groupOwnerIntent < 0 || groupOwnerIntent > 15) { 464 groupOwnerIntent = 7; //default value 465 } 466 args.add("go_intent=" + groupOwnerIntent); 467 468 String command = "P2P_CONNECT "; 469 for (String s : args) command += s + " "; 470 471 return doStringCommand(command); 472 } 473 474 public static boolean p2pCancelConnect() { 475 return doBooleanCommand("P2P_CANCEL"); 476 } 477 478 public static boolean p2pProvisionDiscovery(WifiP2pConfig config) { 479 if (config == null) return false; 480 481 switch (config.wps.setup) { 482 case WpsInfo.PBC: 483 return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " pbc"); 484 case WpsInfo.DISPLAY: 485 //We are doing display, so provision discovery is keypad 486 return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " keypad"); 487 case WpsInfo.KEYPAD: 488 //We are doing keypad, so provision discovery is display 489 return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " display"); 490 default: 491 break; 492 } 493 return false; 494 } 495 496 public static boolean p2pGroupAdd() { 497 return doBooleanCommand("P2P_GROUP_ADD"); 498 } 499 500 public static boolean p2pGroupRemove(String iface) { 501 if (iface == null) return false; 502 return doBooleanCommand("P2P_GROUP_REMOVE " + iface); 503 } 504 505 public static boolean p2pReject(String deviceAddress) { 506 return doBooleanCommand("P2P_REJECT " + deviceAddress); 507 } 508 509 /* Invite a peer to a group */ 510 public static boolean p2pInvite(WifiP2pGroup group, String deviceAddress) { 511 if (deviceAddress == null) return false; 512 513 if (group == null) { 514 return doBooleanCommand("P2P_INVITE peer=" + deviceAddress); 515 } else { 516 return doBooleanCommand("P2P_INVITE group=" + group.getInterface() 517 + " peer=" + deviceAddress + " go_dev_addr=" + group.getOwner().deviceAddress); 518 } 519 } 520 521 /* Reinvoke a persistent connection */ 522 public static boolean p2pReinvoke(int netId, String deviceAddress) { 523 if (deviceAddress == null || netId < 0) return false; 524 525 return doBooleanCommand("P2P_INVITE persistent=" + netId + " peer=" + deviceAddress); 526 } 527 528 529 public static String p2pGetInterfaceAddress(String deviceAddress) { 530 if (deviceAddress == null) return null; 531 532 // "p2p_peer deviceAddress" returns a multi-line result containing 533 // intended_addr=fa:7b:7a:42:82:13 534 String peerInfo = p2pPeer(deviceAddress); 535 if (peerInfo == null) return null; 536 String[] tokens= peerInfo.split("\n"); 537 538 for (String token : tokens) { 539 //TODO: update from interface_addr when wpa_supplicant implementation is fixed 540 if (token.startsWith("intended_addr=")) { 541 String[] nameValue = token.split("="); 542 if (nameValue.length != 2) break; 543 return nameValue[1]; 544 } 545 } 546 return null; 547 } 548 549 public static String p2pGetDeviceAddress() { 550 String status = status(); 551 if (status == null) return ""; 552 553 String[] tokens = status.split("\n"); 554 for (String token : tokens) { 555 if (token.startsWith("p2p_device_address=")) { 556 String[] nameValue = token.split("="); 557 if (nameValue.length != 2) break; 558 return nameValue[1]; 559 } 560 } 561 return ""; 562 } 563 564 public static String p2pPeer(String deviceAddress) { 565 return doStringCommand("P2P_PEER " + deviceAddress); 566 } 567} 568