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 17 package android.net.wifi; 18 19 import android.net.wifi.p2p.WifiP2pConfig; 20 import android.net.wifi.p2p.WifiP2pGroup; 21 import android.text.TextUtils; 22 import android.net.wifi.p2p.nsd.WifiP2pServiceInfo; 23 import android.util.LocalLog; 24 import android.util.Log; 25 26 import java.util.ArrayList; 27 import java.util.List; 28 import java.util.Locale; 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 */ 39 public class WifiNative { 40 41 private static final boolean DBG = false; 42 private final String mTAG; 43 private static final int DEFAULT_GROUP_OWNER_INTENT = 6; 44 45 static final int BLUETOOTH_COEXISTENCE_MODE_ENABLED = 0; 46 static final int BLUETOOTH_COEXISTENCE_MODE_DISABLED = 1; 47 static final int BLUETOOTH_COEXISTENCE_MODE_SENSE = 2; 48 49 static final int SCAN_WITHOUT_CONNECTION_SETUP = 1; 50 static final int SCAN_WITH_CONNECTION_SETUP = 2; 51 52 // Hold this lock before calling supplicant - it is required to 53 // mutually exclude access from Wifi and P2p state machines 54 static final Object mLock = new Object(); 55 56 public final String mInterfaceName; 57 public final String mInterfacePrefix; 58 59 private boolean mSuspendOptEnabled = false; 60 61 public native static boolean loadDriver(); 62 63 public native static boolean isDriverLoaded(); 64 65 public native static boolean unloadDriver(); 66 67 public native static boolean startSupplicant(boolean p2pSupported); 68 69 /* Sends a kill signal to supplicant. To be used when we have lost connection 70 or when the supplicant is hung */ 71 public native static boolean killSupplicant(boolean p2pSupported); 72 73 private native boolean connectToSupplicantNative(); 74 75 private native void closeSupplicantConnectionNative(); 76 77 /** 78 * Wait for the supplicant to send an event, returning the event string. 79 * @return the event string sent by the supplicant. 80 */ 81 private native String waitForEventNative(); 82 83 private native boolean doBooleanCommandNative(String command); 84 85 private native int doIntCommandNative(String command); 86 87 private native String doStringCommandNative(String command); 88 89 public WifiNative(String interfaceName) { 90 mInterfaceName = interfaceName; 91 mTAG = "WifiNative-" + interfaceName; 92 if (!interfaceName.equals("p2p0")) { 93 mInterfacePrefix = "IFNAME=" + interfaceName + " "; 94 } else { 95 // commands for p2p0 interface don't need prefix 96 mInterfacePrefix = ""; 97 } 98 } 99 100 private static final LocalLog mLocalLog = new LocalLog(1024); 101 102 // hold mLock before accessing mCmdIdLock 103 private int mCmdId; 104 105 public LocalLog getLocalLog() { 106 return mLocalLog; 107 } 108 109 private int getNewCmdIdLocked() { 110 return mCmdId++; 111 } 112 113 private void localLog(String s) { 114 if (mLocalLog != null) 115 mLocalLog.log(mInterfaceName + ": " + s); 116 } 117 118 public boolean connectToSupplicant() { 119 // No synchronization necessary .. it is implemented in WifiMonitor 120 localLog(mInterfacePrefix + "connectToSupplicant"); 121 return connectToSupplicantNative(); 122 } 123 124 public void closeSupplicantConnection() { 125 localLog(mInterfacePrefix + "closeSupplicantConnection"); 126 closeSupplicantConnectionNative(); 127 } 128 129 public String waitForEvent() { 130 // No synchronization necessary .. it is implemented in WifiMonitor 131 return waitForEventNative(); 132 } 133 134 private boolean doBooleanCommand(String command) { 135 if (DBG) Log.d(mTAG, "doBoolean: " + command); 136 synchronized (mLock) { 137 int cmdId = getNewCmdIdLocked(); 138 localLog(cmdId + "->" + mInterfacePrefix + command); 139 boolean result = doBooleanCommandNative(mInterfacePrefix + command); 140 localLog(cmdId + "<-" + result); 141 if (DBG) Log.d(mTAG, " returned " + result); 142 return result; 143 } 144 } 145 146 private int doIntCommand(String command) { 147 if (DBG) Log.d(mTAG, "doInt: " + command); 148 synchronized (mLock) { 149 int cmdId = getNewCmdIdLocked(); 150 localLog(cmdId + "->" + mInterfacePrefix + command); 151 int result = doIntCommandNative(mInterfacePrefix + command); 152 localLog(cmdId + "<-" + result); 153 if (DBG) Log.d(mTAG, " returned " + result); 154 return result; 155 } 156 } 157 158 private String doStringCommand(String command) { 159 if (DBG) Log.d(mTAG, "doString: " + command); 160 synchronized (mLock) { 161 int cmdId = getNewCmdIdLocked(); 162 localLog(cmdId + "->" + mInterfacePrefix + command); 163 String result = doStringCommandNative(mInterfacePrefix + command); 164 localLog(cmdId + "<-" + result); 165 if (DBG) Log.d(mTAG, " returned " + result); 166 return result; 167 } 168 } 169 170 private String doStringCommandWithoutLogging(String command) { 171 if (DBG) Log.d(mTAG, "doString: " + command); 172 synchronized (mLock) { 173 return doStringCommandNative(mInterfacePrefix + command); 174 } 175 } 176 177 public boolean ping() { 178 String pong = doStringCommand("PING"); 179 return (pong != null && pong.equals("PONG")); 180 } 181 182 public boolean scan(int type) { 183 if (type == SCAN_WITHOUT_CONNECTION_SETUP) { 184 return doBooleanCommand("SCAN TYPE=ONLY"); 185 } else if (type == SCAN_WITH_CONNECTION_SETUP) { 186 return doBooleanCommand("SCAN"); 187 } else { 188 throw new IllegalArgumentException("Invalid scan type"); 189 } 190 } 191 192 /* Does a graceful shutdown of supplicant. Is a common stop function for both p2p and sta. 193 * 194 * Note that underneath we use a harsh-sounding "terminate" supplicant command 195 * for a graceful stop and a mild-sounding "stop" interface 196 * to kill the process 197 */ 198 public boolean stopSupplicant() { 199 return doBooleanCommand("TERMINATE"); 200 } 201 202 public String listNetworks() { 203 return doStringCommand("LIST_NETWORKS"); 204 } 205 206 public int addNetwork() { 207 return doIntCommand("ADD_NETWORK"); 208 } 209 210 public boolean setNetworkVariable(int netId, String name, String value) { 211 if (TextUtils.isEmpty(name) || TextUtils.isEmpty(value)) return false; 212 return doBooleanCommand("SET_NETWORK " + netId + " " + name + " " + value); 213 } 214 215 public String getNetworkVariable(int netId, String name) { 216 if (TextUtils.isEmpty(name)) return null; 217 218 // GET_NETWORK will likely flood the logs ... 219 return doStringCommandWithoutLogging("GET_NETWORK " + netId + " " + name); 220 } 221 222 public boolean removeNetwork(int netId) { 223 return doBooleanCommand("REMOVE_NETWORK " + netId); 224 } 225 226 public boolean enableNetwork(int netId, boolean disableOthers) { 227 if (disableOthers) { 228 return doBooleanCommand("SELECT_NETWORK " + netId); 229 } else { 230 return doBooleanCommand("ENABLE_NETWORK " + netId); 231 } 232 } 233 234 public boolean disableNetwork(int netId) { 235 return doBooleanCommand("DISABLE_NETWORK " + netId); 236 } 237 238 public boolean reconnect() { 239 return doBooleanCommand("RECONNECT"); 240 } 241 242 public boolean reassociate() { 243 return doBooleanCommand("REASSOCIATE"); 244 } 245 246 public boolean disconnect() { 247 return doBooleanCommand("DISCONNECT"); 248 } 249 250 public String status() { 251 return doStringCommand("STATUS"); 252 } 253 254 public String getMacAddress() { 255 //Macaddr = XX.XX.XX.XX.XX.XX 256 String ret = doStringCommand("DRIVER MACADDR"); 257 if (!TextUtils.isEmpty(ret)) { 258 String[] tokens = ret.split(" = "); 259 if (tokens.length == 2) return tokens[1]; 260 } 261 return null; 262 } 263 264 /** 265 * Format of results: 266 * ================= 267 * id=1 268 * bssid=68:7f:74:d7:1b:6e 269 * freq=2412 270 * level=-43 271 * tsf=1344621975160944 272 * age=2623 273 * flags=[WPA2-PSK-CCMP][WPS][ESS] 274 * ssid=zubyb 275 * ==== 276 * 277 * RANGE=ALL gets all scan results 278 * RANGE=ID- gets results from ID 279 * MASK=<N> see wpa_supplicant/src/common/wpa_ctrl.h for details 280 */ 281 public String scanResults(int sid) { 282 return doStringCommandWithoutLogging("BSS RANGE=" + sid + "- MASK=0x21987"); 283 } 284 285 /** 286 * Format of command 287 * DRIVER WLS_BATCHING SET SCANFREQ=x MSCAN=r BESTN=y CHANNEL=<z, w, t> RTT=s 288 * where x is an ascii representation of an integer number of seconds between scans 289 * r is an ascii representation of an integer number of scans per batch 290 * y is an ascii representation of an integer number of the max AP to remember per scan 291 * z, w, t represent a 1..n size list of channel numbers and/or 'A', 'B' values 292 * indicating entire ranges of channels 293 * s is an ascii representation of an integer number of highest-strength AP 294 * for which we'd like approximate distance reported 295 * 296 * The return value is an ascii integer representing a guess of the number of scans 297 * the firmware can remember before it runs out of buffer space or -1 on error 298 */ 299 public String setBatchedScanSettings(BatchedScanSettings settings) { 300 if (settings == null) { 301 return doStringCommand("DRIVER WLS_BATCHING STOP"); 302 } 303 String cmd = "DRIVER WLS_BATCHING SET SCANFREQ=" + settings.scanIntervalSec; 304 cmd += " MSCAN=" + settings.maxScansPerBatch; 305 if (settings.maxApPerScan != BatchedScanSettings.UNSPECIFIED) { 306 cmd += " BESTN=" + settings.maxApPerScan; 307 } 308 if (settings.channelSet != null && !settings.channelSet.isEmpty()) { 309 cmd += " CHANNEL=<"; 310 int i = 0; 311 for (String channel : settings.channelSet) { 312 cmd += (i > 0 ? "," : "") + channel; 313 ++i; 314 } 315 cmd += ">"; 316 } 317 if (settings.maxApForDistance != BatchedScanSettings.UNSPECIFIED) { 318 cmd += " RTT=" + settings.maxApForDistance; 319 } 320 return doStringCommand(cmd); 321 } 322 323 public String getBatchedScanResults() { 324 return doStringCommand("DRIVER WLS_BATCHING GET"); 325 } 326 327 public boolean startDriver() { 328 return doBooleanCommand("DRIVER START"); 329 } 330 331 public boolean stopDriver() { 332 return doBooleanCommand("DRIVER STOP"); 333 } 334 335 336 /** 337 * Start filtering out Multicast V4 packets 338 * @return {@code true} if the operation succeeded, {@code false} otherwise 339 * 340 * Multicast filtering rules work as follows: 341 * 342 * The driver can filter multicast (v4 and/or v6) and broadcast packets when in 343 * a power optimized mode (typically when screen goes off). 344 * 345 * In order to prevent the driver from filtering the multicast/broadcast packets, we have to 346 * add a DRIVER RXFILTER-ADD rule followed by DRIVER RXFILTER-START to make the rule effective 347 * 348 * DRIVER RXFILTER-ADD Num 349 * where Num = 0 - Unicast, 1 - Broadcast, 2 - Mutil4 or 3 - Multi6 350 * 351 * and DRIVER RXFILTER-START 352 * In order to stop the usage of these rules, we do 353 * 354 * DRIVER RXFILTER-STOP 355 * DRIVER RXFILTER-REMOVE Num 356 * where Num is as described for RXFILTER-ADD 357 * 358 * The SETSUSPENDOPT driver command overrides the filtering rules 359 */ 360 public boolean startFilteringMulticastV4Packets() { 361 return doBooleanCommand("DRIVER RXFILTER-STOP") 362 && doBooleanCommand("DRIVER RXFILTER-REMOVE 2") 363 && doBooleanCommand("DRIVER RXFILTER-START"); 364 } 365 366 /** 367 * Stop filtering out Multicast V4 packets. 368 * @return {@code true} if the operation succeeded, {@code false} otherwise 369 */ 370 public boolean stopFilteringMulticastV4Packets() { 371 return doBooleanCommand("DRIVER RXFILTER-STOP") 372 && doBooleanCommand("DRIVER RXFILTER-ADD 2") 373 && doBooleanCommand("DRIVER RXFILTER-START"); 374 } 375 376 /** 377 * Start filtering out Multicast V6 packets 378 * @return {@code true} if the operation succeeded, {@code false} otherwise 379 */ 380 public boolean startFilteringMulticastV6Packets() { 381 return doBooleanCommand("DRIVER RXFILTER-STOP") 382 && doBooleanCommand("DRIVER RXFILTER-REMOVE 3") 383 && doBooleanCommand("DRIVER RXFILTER-START"); 384 } 385 386 /** 387 * Stop filtering out Multicast V6 packets. 388 * @return {@code true} if the operation succeeded, {@code false} otherwise 389 */ 390 public boolean stopFilteringMulticastV6Packets() { 391 return doBooleanCommand("DRIVER RXFILTER-STOP") 392 && doBooleanCommand("DRIVER RXFILTER-ADD 3") 393 && doBooleanCommand("DRIVER RXFILTER-START"); 394 } 395 396 public int getBand() { 397 String ret = doStringCommand("DRIVER GETBAND"); 398 if (!TextUtils.isEmpty(ret)) { 399 //reply is "BAND X" where X is the band 400 String[] tokens = ret.split(" "); 401 try { 402 if (tokens.length == 2) return Integer.parseInt(tokens[1]); 403 } catch (NumberFormatException e) { 404 return -1; 405 } 406 } 407 return -1; 408 } 409 410 public boolean setBand(int band) { 411 return doBooleanCommand("DRIVER SETBAND " + band); 412 } 413 414 /** 415 * Sets the bluetooth coexistence mode. 416 * 417 * @param mode One of {@link #BLUETOOTH_COEXISTENCE_MODE_DISABLED}, 418 * {@link #BLUETOOTH_COEXISTENCE_MODE_ENABLED}, or 419 * {@link #BLUETOOTH_COEXISTENCE_MODE_SENSE}. 420 * @return Whether the mode was successfully set. 421 */ 422 public boolean setBluetoothCoexistenceMode(int mode) { 423 return doBooleanCommand("DRIVER BTCOEXMODE " + mode); 424 } 425 426 /** 427 * Enable or disable Bluetooth coexistence scan mode. When this mode is on, 428 * some of the low-level scan parameters used by the driver are changed to 429 * reduce interference with A2DP streaming. 430 * 431 * @param isSet whether to enable or disable this mode 432 * @return {@code true} if the command succeeded, {@code false} otherwise. 433 */ 434 public boolean setBluetoothCoexistenceScanMode(boolean setCoexScanMode) { 435 if (setCoexScanMode) { 436 return doBooleanCommand("DRIVER BTCOEXSCAN-START"); 437 } else { 438 return doBooleanCommand("DRIVER BTCOEXSCAN-STOP"); 439 } 440 } 441 442 public boolean saveConfig() { 443 return doBooleanCommand("SAVE_CONFIG"); 444 } 445 446 public boolean addToBlacklist(String bssid) { 447 if (TextUtils.isEmpty(bssid)) return false; 448 return doBooleanCommand("BLACKLIST " + bssid); 449 } 450 451 public boolean clearBlacklist() { 452 return doBooleanCommand("BLACKLIST clear"); 453 } 454 455 public boolean setSuspendOptimizations(boolean enabled) { 456 if (mSuspendOptEnabled == enabled) return true; 457 mSuspendOptEnabled = enabled; 458 if (enabled) { 459 return doBooleanCommand("DRIVER SETSUSPENDMODE 1"); 460 } else { 461 return doBooleanCommand("DRIVER SETSUSPENDMODE 0"); 462 } 463 } 464 465 public boolean setCountryCode(String countryCode) { 466 return doBooleanCommand("DRIVER COUNTRY " + countryCode.toUpperCase(Locale.ROOT)); 467 } 468 469 public void enableBackgroundScan(boolean enable) { 470 if (enable) { 471 doBooleanCommand("SET pno 1"); 472 } else { 473 doBooleanCommand("SET pno 0"); 474 } 475 } 476 477 public void setScanInterval(int scanInterval) { 478 doBooleanCommand("SCAN_INTERVAL " + scanInterval); 479 } 480 481 public void startTdls(String macAddr, boolean enable) { 482 if (enable) { 483 doBooleanCommand("TDLS_DISCOVER " + macAddr); 484 doBooleanCommand("TDLS_SETUP " + macAddr); 485 } else { 486 doBooleanCommand("TDLS_TEARDOWN " + macAddr); 487 } 488 } 489 490 /** Example output: 491 * RSSI=-65 492 * LINKSPEED=48 493 * NOISE=9999 494 * FREQUENCY=0 495 */ 496 public String signalPoll() { 497 return doStringCommandWithoutLogging("SIGNAL_POLL"); 498 } 499 500 /** Example outout: 501 * TXGOOD=396 502 * TXBAD=1 503 */ 504 public String pktcntPoll() { 505 return doStringCommand("PKTCNT_POLL"); 506 } 507 508 public void bssFlush() { 509 doBooleanCommand("BSS_FLUSH 0"); 510 } 511 512 public boolean startWpsPbc(String bssid) { 513 if (TextUtils.isEmpty(bssid)) { 514 return doBooleanCommand("WPS_PBC"); 515 } else { 516 return doBooleanCommand("WPS_PBC " + bssid); 517 } 518 } 519 520 public boolean startWpsPbc(String iface, String bssid) { 521 synchronized (mLock) { 522 if (TextUtils.isEmpty(bssid)) { 523 return doBooleanCommandNative("IFNAME=" + iface + " WPS_PBC"); 524 } else { 525 return doBooleanCommandNative("IFNAME=" + iface + " WPS_PBC " + bssid); 526 } 527 } 528 } 529 530 public boolean startWpsPinKeypad(String pin) { 531 if (TextUtils.isEmpty(pin)) return false; 532 return doBooleanCommand("WPS_PIN any " + pin); 533 } 534 535 public boolean startWpsPinKeypad(String iface, String pin) { 536 if (TextUtils.isEmpty(pin)) return false; 537 synchronized (mLock) { 538 return doBooleanCommandNative("IFNAME=" + iface + " WPS_PIN any " + pin); 539 } 540 } 541 542 543 public String startWpsPinDisplay(String bssid) { 544 if (TextUtils.isEmpty(bssid)) { 545 return doStringCommand("WPS_PIN any"); 546 } else { 547 return doStringCommand("WPS_PIN " + bssid); 548 } 549 } 550 551 public String startWpsPinDisplay(String iface, String bssid) { 552 synchronized (mLock) { 553 if (TextUtils.isEmpty(bssid)) { 554 return doStringCommandNative("IFNAME=" + iface + " WPS_PIN any"); 555 } else { 556 return doStringCommandNative("IFNAME=" + iface + " WPS_PIN " + bssid); 557 } 558 } 559 } 560 561 /* Configures an access point connection */ 562 public boolean startWpsRegistrar(String bssid, String pin) { 563 if (TextUtils.isEmpty(bssid) || TextUtils.isEmpty(pin)) return false; 564 return doBooleanCommand("WPS_REG " + bssid + " " + pin); 565 } 566 567 public boolean cancelWps() { 568 return doBooleanCommand("WPS_CANCEL"); 569 } 570 571 public boolean setPersistentReconnect(boolean enabled) { 572 int value = (enabled == true) ? 1 : 0; 573 return doBooleanCommand("SET persistent_reconnect " + value); 574 } 575 576 public boolean setDeviceName(String name) { 577 return doBooleanCommand("SET device_name " + name); 578 } 579 580 public boolean setDeviceType(String type) { 581 return doBooleanCommand("SET device_type " + type); 582 } 583 584 public boolean setConfigMethods(String cfg) { 585 return doBooleanCommand("SET config_methods " + cfg); 586 } 587 588 public boolean setManufacturer(String value) { 589 return doBooleanCommand("SET manufacturer " + value); 590 } 591 592 public boolean setModelName(String value) { 593 return doBooleanCommand("SET model_name " + value); 594 } 595 596 public boolean setModelNumber(String value) { 597 return doBooleanCommand("SET model_number " + value); 598 } 599 600 public boolean setSerialNumber(String value) { 601 return doBooleanCommand("SET serial_number " + value); 602 } 603 604 public boolean setP2pSsidPostfix(String postfix) { 605 return doBooleanCommand("SET p2p_ssid_postfix " + postfix); 606 } 607 608 public boolean setP2pGroupIdle(String iface, int time) { 609 synchronized (mLock) { 610 return doBooleanCommandNative("IFNAME=" + iface + " SET p2p_group_idle " + time); 611 } 612 } 613 614 public void setPowerSave(boolean enabled) { 615 if (enabled) { 616 doBooleanCommand("SET ps 1"); 617 } else { 618 doBooleanCommand("SET ps 0"); 619 } 620 } 621 622 public boolean setP2pPowerSave(String iface, boolean enabled) { 623 synchronized (mLock) { 624 if (enabled) { 625 return doBooleanCommandNative("IFNAME=" + iface + " P2P_SET ps 1"); 626 } else { 627 return doBooleanCommandNative("IFNAME=" + iface + " P2P_SET ps 0"); 628 } 629 } 630 } 631 632 public boolean setWfdEnable(boolean enable) { 633 return doBooleanCommand("SET wifi_display " + (enable ? "1" : "0")); 634 } 635 636 public boolean setWfdDeviceInfo(String hex) { 637 return doBooleanCommand("WFD_SUBELEM_SET 0 " + hex); 638 } 639 640 /** 641 * "sta" prioritizes STA connection over P2P and "p2p" prioritizes 642 * P2P connection over STA 643 */ 644 public boolean setConcurrencyPriority(String s) { 645 return doBooleanCommand("P2P_SET conc_pref " + s); 646 } 647 648 public boolean p2pFind() { 649 return doBooleanCommand("P2P_FIND"); 650 } 651 652 public boolean p2pFind(int timeout) { 653 if (timeout <= 0) { 654 return p2pFind(); 655 } 656 return doBooleanCommand("P2P_FIND " + timeout); 657 } 658 659 public boolean p2pStopFind() { 660 return doBooleanCommand("P2P_STOP_FIND"); 661 } 662 663 public boolean p2pListen() { 664 return doBooleanCommand("P2P_LISTEN"); 665 } 666 667 public boolean p2pListen(int timeout) { 668 if (timeout <= 0) { 669 return p2pListen(); 670 } 671 return doBooleanCommand("P2P_LISTEN " + timeout); 672 } 673 674 public boolean p2pExtListen(boolean enable, int period, int interval) { 675 if (enable && interval < period) { 676 return false; 677 } 678 return doBooleanCommand("P2P_EXT_LISTEN" 679 + (enable ? (" " + period + " " + interval) : "")); 680 } 681 682 public boolean p2pSetChannel(int lc, int oc) { 683 if (DBG) Log.d(mTAG, "p2pSetChannel: lc="+lc+", oc="+oc); 684 685 if (lc >=1 && lc <= 11) { 686 if (!doBooleanCommand("P2P_SET listen_channel " + lc)) { 687 return false; 688 } 689 } else if (lc != 0) { 690 return false; 691 } 692 693 if (oc >= 1 && oc <= 165 ) { 694 int freq = (oc <= 14 ? 2407 : 5000) + oc * 5; 695 return doBooleanCommand("P2P_SET disallow_freq 1000-" 696 + (freq - 5) + "," + (freq + 5) + "-6000"); 697 } else if (oc == 0) { 698 /* oc==0 disables "P2P_SET disallow_freq" (enables all freqs) */ 699 return doBooleanCommand("P2P_SET disallow_freq \"\""); 700 } 701 702 return false; 703 } 704 705 public boolean p2pFlush() { 706 return doBooleanCommand("P2P_FLUSH"); 707 } 708 709 /* p2p_connect <peer device address> <pbc|pin|PIN#> [label|display|keypad] 710 [persistent] [join|auth] [go_intent=<0..15>] [freq=<in MHz>] */ 711 public String p2pConnect(WifiP2pConfig config, boolean joinExistingGroup) { 712 if (config == null) return null; 713 List<String> args = new ArrayList<String>(); 714 WpsInfo wps = config.wps; 715 args.add(config.deviceAddress); 716 717 switch (wps.setup) { 718 case WpsInfo.PBC: 719 args.add("pbc"); 720 break; 721 case WpsInfo.DISPLAY: 722 if (TextUtils.isEmpty(wps.pin)) { 723 args.add("pin"); 724 } else { 725 args.add(wps.pin); 726 } 727 args.add("display"); 728 break; 729 case WpsInfo.KEYPAD: 730 args.add(wps.pin); 731 args.add("keypad"); 732 break; 733 case WpsInfo.LABEL: 734 args.add(wps.pin); 735 args.add("label"); 736 default: 737 break; 738 } 739 740 if (config.netId == WifiP2pGroup.PERSISTENT_NET_ID) { 741 args.add("persistent"); 742 } 743 744 if (joinExistingGroup) { 745 args.add("join"); 746 } else { 747 //TODO: This can be adapted based on device plugged in state and 748 //device battery state 749 int groupOwnerIntent = config.groupOwnerIntent; 750 if (groupOwnerIntent < 0 || groupOwnerIntent > 15) { 751 groupOwnerIntent = DEFAULT_GROUP_OWNER_INTENT; 752 } 753 args.add("go_intent=" + groupOwnerIntent); 754 } 755 756 String command = "P2P_CONNECT "; 757 for (String s : args) command += s + " "; 758 759 return doStringCommand(command); 760 } 761 762 public boolean p2pCancelConnect() { 763 return doBooleanCommand("P2P_CANCEL"); 764 } 765 766 public boolean p2pProvisionDiscovery(WifiP2pConfig config) { 767 if (config == null) return false; 768 769 switch (config.wps.setup) { 770 case WpsInfo.PBC: 771 return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " pbc"); 772 case WpsInfo.DISPLAY: 773 //We are doing display, so provision discovery is keypad 774 return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " keypad"); 775 case WpsInfo.KEYPAD: 776 //We are doing keypad, so provision discovery is display 777 return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " display"); 778 default: 779 break; 780 } 781 return false; 782 } 783 784 public boolean p2pGroupAdd(boolean persistent) { 785 if (persistent) { 786 return doBooleanCommand("P2P_GROUP_ADD persistent"); 787 } 788 return doBooleanCommand("P2P_GROUP_ADD"); 789 } 790 791 public boolean p2pGroupAdd(int netId) { 792 return doBooleanCommand("P2P_GROUP_ADD persistent=" + netId); 793 } 794 795 public boolean p2pGroupRemove(String iface) { 796 if (TextUtils.isEmpty(iface)) return false; 797 synchronized (mLock) { 798 return doBooleanCommandNative("IFNAME=" + iface + " P2P_GROUP_REMOVE " + iface); 799 } 800 } 801 802 public boolean p2pReject(String deviceAddress) { 803 return doBooleanCommand("P2P_REJECT " + deviceAddress); 804 } 805 806 /* Invite a peer to a group */ 807 public boolean p2pInvite(WifiP2pGroup group, String deviceAddress) { 808 if (TextUtils.isEmpty(deviceAddress)) return false; 809 810 if (group == null) { 811 return doBooleanCommand("P2P_INVITE peer=" + deviceAddress); 812 } else { 813 return doBooleanCommand("P2P_INVITE group=" + group.getInterface() 814 + " peer=" + deviceAddress + " go_dev_addr=" + group.getOwner().deviceAddress); 815 } 816 } 817 818 /* Reinvoke a persistent connection */ 819 public boolean p2pReinvoke(int netId, String deviceAddress) { 820 if (TextUtils.isEmpty(deviceAddress) || netId < 0) return false; 821 822 return doBooleanCommand("P2P_INVITE persistent=" + netId + " peer=" + deviceAddress); 823 } 824 825 public String p2pGetSsid(String deviceAddress) { 826 return p2pGetParam(deviceAddress, "oper_ssid"); 827 } 828 829 public String p2pGetDeviceAddress() { 830 String status = status(); 831 if (status == null) return ""; 832 833 String[] tokens = status.split("\n"); 834 for (String token : tokens) { 835 if (token.startsWith("p2p_device_address=")) { 836 String[] nameValue = token.split("="); 837 if (nameValue.length != 2) break; 838 return nameValue[1]; 839 } 840 } 841 return ""; 842 } 843 844 public int getGroupCapability(String deviceAddress) { 845 int gc = 0; 846 if (TextUtils.isEmpty(deviceAddress)) return gc; 847 String peerInfo = p2pPeer(deviceAddress); 848 if (TextUtils.isEmpty(peerInfo)) return gc; 849 850 String[] tokens = peerInfo.split("\n"); 851 for (String token : tokens) { 852 if (token.startsWith("group_capab=")) { 853 String[] nameValue = token.split("="); 854 if (nameValue.length != 2) break; 855 try { 856 return Integer.decode(nameValue[1]); 857 } catch(NumberFormatException e) { 858 return gc; 859 } 860 } 861 } 862 return gc; 863 } 864 865 public String p2pPeer(String deviceAddress) { 866 return doStringCommand("P2P_PEER " + deviceAddress); 867 } 868 869 private String p2pGetParam(String deviceAddress, String key) { 870 if (deviceAddress == null) return null; 871 872 String peerInfo = p2pPeer(deviceAddress); 873 if (peerInfo == null) return null; 874 String[] tokens= peerInfo.split("\n"); 875 876 key += "="; 877 for (String token : tokens) { 878 if (token.startsWith(key)) { 879 String[] nameValue = token.split("="); 880 if (nameValue.length != 2) break; 881 return nameValue[1]; 882 } 883 } 884 return null; 885 } 886 887 public boolean p2pServiceAdd(WifiP2pServiceInfo servInfo) { 888 /* 889 * P2P_SERVICE_ADD bonjour <query hexdump> <RDATA hexdump> 890 * P2P_SERVICE_ADD upnp <version hex> <service> 891 * 892 * e.g) 893 * [Bonjour] 894 * # IP Printing over TCP (PTR) (RDATA=MyPrinter._ipp._tcp.local.) 895 * P2P_SERVICE_ADD bonjour 045f697070c00c000c01 094d795072696e746572c027 896 * # IP Printing over TCP (TXT) (RDATA=txtvers=1,pdl=application/postscript) 897 * P2P_SERVICE_ADD bonjour 096d797072696e746572045f697070c00c001001 898 * 09747874766572733d311a70646c3d6170706c69636174696f6e2f706f7374736372797074 899 * 900 * [UPnP] 901 * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012 902 * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice 903 * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::urn:schemas-upnp 904 * -org:device:InternetGatewayDevice:1 905 * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9322-123456789012::urn:schemas-upnp 906 * -org:service:ContentDirectory:2 907 */ 908 for (String s : servInfo.getSupplicantQueryList()) { 909 String command = "P2P_SERVICE_ADD"; 910 command += (" " + s); 911 if (!doBooleanCommand(command)) { 912 return false; 913 } 914 } 915 return true; 916 } 917 918 public boolean p2pServiceDel(WifiP2pServiceInfo servInfo) { 919 /* 920 * P2P_SERVICE_DEL bonjour <query hexdump> 921 * P2P_SERVICE_DEL upnp <version hex> <service> 922 */ 923 for (String s : servInfo.getSupplicantQueryList()) { 924 String command = "P2P_SERVICE_DEL "; 925 926 String[] data = s.split(" "); 927 if (data.length < 2) { 928 return false; 929 } 930 if ("upnp".equals(data[0])) { 931 command += s; 932 } else if ("bonjour".equals(data[0])) { 933 command += data[0]; 934 command += (" " + data[1]); 935 } else { 936 return false; 937 } 938 if (!doBooleanCommand(command)) { 939 return false; 940 } 941 } 942 return true; 943 } 944 945 public boolean p2pServiceFlush() { 946 return doBooleanCommand("P2P_SERVICE_FLUSH"); 947 } 948 949 public String p2pServDiscReq(String addr, String query) { 950 String command = "P2P_SERV_DISC_REQ"; 951 command += (" " + addr); 952 command += (" " + query); 953 954 return doStringCommand(command); 955 } 956 957 public boolean p2pServDiscCancelReq(String id) { 958 return doBooleanCommand("P2P_SERV_DISC_CANCEL_REQ " + id); 959 } 960 961 /* Set the current mode of miracast operation. 962 * 0 = disabled 963 * 1 = operating as source 964 * 2 = operating as sink 965 */ 966 public void setMiracastMode(int mode) { 967 // Note: optional feature on the driver. It is ok for this to fail. 968 doBooleanCommand("DRIVER MIRACAST " + mode); 969 } 970 } 971