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.net.wifi.p2p.WifiP2pDevice; 22 import android.text.TextUtils; 23 import android.net.wifi.p2p.nsd.WifiP2pServiceInfo; 24 import android.net.wifi.p2p.nsd.WifiP2pServiceRequest; 25 import android.util.Log; 26 27 import java.io.InputStream; 28 import java.lang.Process; 29 import java.util.ArrayList; 30 import java.util.List; 31 32 /** 33 * Native calls for bring up/shut down of the supplicant daemon and for 34 * sending requests to the supplicant daemon 35 * 36 * waitForEvent() is called on the monitor thread for events. All other methods 37 * must be serialized from the framework. 38 * 39 * {@hide} 40 */ 41 public class WifiNative { 42 43 private static final boolean DBG = false; 44 private final String mTAG; 45 private static final int DEFAULT_GROUP_OWNER_INTENT = 7; 46 47 static final int BLUETOOTH_COEXISTENCE_MODE_ENABLED = 0; 48 static final int BLUETOOTH_COEXISTENCE_MODE_DISABLED = 1; 49 static final int BLUETOOTH_COEXISTENCE_MODE_SENSE = 2; 50 51 String mInterface = ""; 52 53 public native static boolean loadDriver(); 54 55 public native static boolean isDriverLoaded(); 56 57 public native static boolean unloadDriver(); 58 59 public native static boolean startSupplicant(boolean p2pSupported); 60 61 /* Sends a kill signal to supplicant. To be used when we have lost connection 62 or when the supplicant is hung */ 63 public native static boolean killSupplicant(); 64 65 private native boolean connectToSupplicant(String iface); 66 67 private native void closeSupplicantConnection(String iface); 68 69 /** 70 * Wait for the supplicant to send an event, returning the event string. 71 * @return the event string sent by the supplicant. 72 */ 73 private native String waitForEvent(String iface); 74 75 private native boolean doBooleanCommand(String iface, String command); 76 77 private native int doIntCommand(String iface, String command); 78 79 private native String doStringCommand(String iface, String command); 80 81 public WifiNative(String iface) { 82 mInterface = iface; 83 mTAG = "WifiNative-" + iface; 84 } 85 86 public boolean connectToSupplicant() { 87 return connectToSupplicant(mInterface); 88 } 89 90 public void closeSupplicantConnection() { 91 closeSupplicantConnection(mInterface); 92 } 93 94 public String waitForEvent() { 95 return waitForEvent(mInterface); 96 } 97 98 private boolean doBooleanCommand(String command) { 99 if (DBG) Log.d(mTAG, "doBoolean: " + command); 100 return doBooleanCommand(mInterface, command); 101 } 102 103 private int doIntCommand(String command) { 104 if (DBG) Log.d(mTAG, "doInt: " + command); 105 return doIntCommand(mInterface, command); 106 } 107 108 private String doStringCommand(String command) { 109 if (DBG) Log.d(mTAG, "doString: " + command); 110 return doStringCommand(mInterface, command); 111 } 112 113 public boolean ping() { 114 String pong = doStringCommand("PING"); 115 return (pong != null && pong.equals("PONG")); 116 } 117 118 public boolean scan() { 119 return doBooleanCommand("SCAN"); 120 } 121 122 public boolean setScanMode(boolean setActive) { 123 if (setActive) { 124 return doBooleanCommand("DRIVER SCAN-ACTIVE"); 125 } else { 126 return doBooleanCommand("DRIVER SCAN-PASSIVE"); 127 } 128 } 129 130 /* Does a graceful shutdown of supplicant. Is a common stop function for both p2p and sta. 131 * 132 * Note that underneath we use a harsh-sounding "terminate" supplicant command 133 * for a graceful stop and a mild-sounding "stop" interface 134 * to kill the process 135 */ 136 public boolean stopSupplicant() { 137 return doBooleanCommand("TERMINATE"); 138 } 139 140 public String listNetworks() { 141 return doStringCommand("LIST_NETWORKS"); 142 } 143 144 public int addNetwork() { 145 return doIntCommand("ADD_NETWORK"); 146 } 147 148 public boolean setNetworkVariable(int netId, String name, String value) { 149 if (TextUtils.isEmpty(name) || TextUtils.isEmpty(value)) return false; 150 return doBooleanCommand("SET_NETWORK " + netId + " " + name + " " + value); 151 } 152 153 public String getNetworkVariable(int netId, String name) { 154 if (TextUtils.isEmpty(name)) return null; 155 return doStringCommand("GET_NETWORK " + netId + " " + name); 156 } 157 158 public boolean removeNetwork(int netId) { 159 return doBooleanCommand("REMOVE_NETWORK " + netId); 160 } 161 162 public boolean enableNetwork(int netId, boolean disableOthers) { 163 if (disableOthers) { 164 return doBooleanCommand("SELECT_NETWORK " + netId); 165 } else { 166 return doBooleanCommand("ENABLE_NETWORK " + netId); 167 } 168 } 169 170 public boolean disableNetwork(int netId) { 171 return doBooleanCommand("DISABLE_NETWORK " + netId); 172 } 173 174 public boolean reconnect() { 175 return doBooleanCommand("RECONNECT"); 176 } 177 178 public boolean reassociate() { 179 return doBooleanCommand("REASSOCIATE"); 180 } 181 182 public boolean disconnect() { 183 return doBooleanCommand("DISCONNECT"); 184 } 185 186 public String status() { 187 return doStringCommand("STATUS"); 188 } 189 190 public String getMacAddress() { 191 //Macaddr = XX.XX.XX.XX.XX.XX 192 String ret = doStringCommand("DRIVER MACADDR"); 193 if (!TextUtils.isEmpty(ret)) { 194 String[] tokens = ret.split(" = "); 195 if (tokens.length == 2) return tokens[1]; 196 } 197 return null; 198 } 199 200 public String scanResults() { 201 return doStringCommand("SCAN_RESULTS"); 202 } 203 204 public boolean startDriver() { 205 return doBooleanCommand("DRIVER START"); 206 } 207 208 public boolean stopDriver() { 209 return doBooleanCommand("DRIVER STOP"); 210 } 211 212 213 /** 214 * Start filtering out Multicast V4 packets 215 * @return {@code true} if the operation succeeded, {@code false} otherwise 216 * 217 * Multicast filtering rules work as follows: 218 * 219 * The driver can filter multicast (v4 and/or v6) and broadcast packets when in 220 * a power optimized mode (typically when screen goes off). 221 * 222 * In order to prevent the driver from filtering the multicast/broadcast packets, we have to 223 * add a DRIVER RXFILTER-ADD rule followed by DRIVER RXFILTER-START to make the rule effective 224 * 225 * DRIVER RXFILTER-ADD Num 226 * where Num = 0 - Unicast, 1 - Broadcast, 2 - Mutil4 or 3 - Multi6 227 * 228 * and DRIVER RXFILTER-START 229 * In order to stop the usage of these rules, we do 230 * 231 * DRIVER RXFILTER-STOP 232 * DRIVER RXFILTER-REMOVE Num 233 * where Num is as described for RXFILTER-ADD 234 * 235 * The SETSUSPENDOPT driver command overrides the filtering rules 236 */ 237 public boolean startFilteringMulticastV4Packets() { 238 return doBooleanCommand("DRIVER RXFILTER-STOP") 239 && doBooleanCommand("DRIVER RXFILTER-REMOVE 2") 240 && doBooleanCommand("DRIVER RXFILTER-START"); 241 } 242 243 /** 244 * Stop filtering out Multicast V4 packets. 245 * @return {@code true} if the operation succeeded, {@code false} otherwise 246 */ 247 public boolean stopFilteringMulticastV4Packets() { 248 return doBooleanCommand("DRIVER RXFILTER-STOP") 249 && doBooleanCommand("DRIVER RXFILTER-ADD 2") 250 && doBooleanCommand("DRIVER RXFILTER-START"); 251 } 252 253 /** 254 * Start filtering out Multicast V6 packets 255 * @return {@code true} if the operation succeeded, {@code false} otherwise 256 */ 257 public boolean startFilteringMulticastV6Packets() { 258 return doBooleanCommand("DRIVER RXFILTER-STOP") 259 && doBooleanCommand("DRIVER RXFILTER-REMOVE 3") 260 && doBooleanCommand("DRIVER RXFILTER-START"); 261 } 262 263 /** 264 * Stop filtering out Multicast V6 packets. 265 * @return {@code true} if the operation succeeded, {@code false} otherwise 266 */ 267 public boolean stopFilteringMulticastV6Packets() { 268 return doBooleanCommand("DRIVER RXFILTER-STOP") 269 && doBooleanCommand("DRIVER RXFILTER-ADD 3") 270 && doBooleanCommand("DRIVER RXFILTER-START"); 271 } 272 273 public int getBand() { 274 String ret = doStringCommand("DRIVER GETBAND"); 275 if (!TextUtils.isEmpty(ret)) { 276 //reply is "BAND X" where X is the band 277 String[] tokens = ret.split(" "); 278 try { 279 if (tokens.length == 2) return Integer.parseInt(tokens[1]); 280 } catch (NumberFormatException e) { 281 return -1; 282 } 283 } 284 return -1; 285 } 286 287 public boolean setBand(int band) { 288 return doBooleanCommand("DRIVER SETBAND " + band); 289 } 290 291 /** 292 * Sets the bluetooth coexistence mode. 293 * 294 * @param mode One of {@link #BLUETOOTH_COEXISTENCE_MODE_DISABLED}, 295 * {@link #BLUETOOTH_COEXISTENCE_MODE_ENABLED}, or 296 * {@link #BLUETOOTH_COEXISTENCE_MODE_SENSE}. 297 * @return Whether the mode was successfully set. 298 */ 299 public boolean setBluetoothCoexistenceMode(int mode) { 300 return doBooleanCommand("DRIVER BTCOEXMODE " + mode); 301 } 302 303 /** 304 * Enable or disable Bluetooth coexistence scan mode. When this mode is on, 305 * some of the low-level scan parameters used by the driver are changed to 306 * reduce interference with A2DP streaming. 307 * 308 * @param isSet whether to enable or disable this mode 309 * @return {@code true} if the command succeeded, {@code false} otherwise. 310 */ 311 public boolean setBluetoothCoexistenceScanMode(boolean setCoexScanMode) { 312 if (setCoexScanMode) { 313 return doBooleanCommand("DRIVER BTCOEXSCAN-START"); 314 } else { 315 return doBooleanCommand("DRIVER BTCOEXSCAN-STOP"); 316 } 317 } 318 319 public boolean saveConfig() { 320 // Make sure we never write out a value for AP_SCAN other than 1 321 return doBooleanCommand("AP_SCAN 1") && doBooleanCommand("SAVE_CONFIG"); 322 } 323 324 public boolean setScanResultHandling(int mode) { 325 return doBooleanCommand("AP_SCAN " + mode); 326 } 327 328 public boolean addToBlacklist(String bssid) { 329 if (TextUtils.isEmpty(bssid)) return false; 330 return doBooleanCommand("BLACKLIST " + bssid); 331 } 332 333 public boolean clearBlacklist() { 334 return doBooleanCommand("BLACKLIST clear"); 335 } 336 337 public boolean setSuspendOptimizations(boolean enabled) { 338 if (enabled) { 339 return doBooleanCommand("DRIVER SETSUSPENDMODE 1"); 340 } else { 341 return doBooleanCommand("DRIVER SETSUSPENDMODE 0"); 342 } 343 } 344 345 public boolean setCountryCode(String countryCode) { 346 return doBooleanCommand("DRIVER COUNTRY " + countryCode); 347 } 348 349 public void enableBackgroundScan(boolean enable) { 350 if (enable) { 351 doBooleanCommand("SET pno 1"); 352 } else { 353 doBooleanCommand("SET pno 0"); 354 } 355 } 356 357 public void setScanInterval(int scanInterval) { 358 doBooleanCommand("SCAN_INTERVAL " + scanInterval); 359 } 360 361 /** Example output: 362 * RSSI=-65 363 * LINKSPEED=48 364 * NOISE=9999 365 * FREQUENCY=0 366 */ 367 public String signalPoll() { 368 return doStringCommand("SIGNAL_POLL"); 369 } 370 371 public boolean startWpsPbc(String bssid) { 372 if (TextUtils.isEmpty(bssid)) { 373 return doBooleanCommand("WPS_PBC"); 374 } else { 375 return doBooleanCommand("WPS_PBC " + bssid); 376 } 377 } 378 379 public boolean startWpsPbc(String iface, String bssid) { 380 if (TextUtils.isEmpty(bssid)) { 381 return doBooleanCommand("WPS_PBC interface=" + iface); 382 } else { 383 return doBooleanCommand("WPS_PBC interface=" + iface + " " + bssid); 384 } 385 } 386 387 public boolean startWpsPinKeypad(String pin) { 388 if (TextUtils.isEmpty(pin)) return false; 389 return doBooleanCommand("WPS_PIN any " + pin); 390 } 391 392 public boolean startWpsPinKeypad(String iface, String pin) { 393 if (TextUtils.isEmpty(pin)) return false; 394 return doBooleanCommand("WPS_PIN interface=" + iface + " any " + pin); 395 } 396 397 398 public String startWpsPinDisplay(String bssid) { 399 if (TextUtils.isEmpty(bssid)) { 400 return doStringCommand("WPS_PIN any"); 401 } else { 402 return doStringCommand("WPS_PIN " + bssid); 403 } 404 } 405 406 public String startWpsPinDisplay(String iface, String bssid) { 407 if (TextUtils.isEmpty(bssid)) { 408 return doStringCommand("WPS_PIN interface=" + iface + " any"); 409 } else { 410 return doStringCommand("WPS_PIN interface=" + iface + " " + bssid); 411 } 412 } 413 414 /* Configures an access point connection */ 415 public boolean startWpsRegistrar(String bssid, String pin) { 416 if (TextUtils.isEmpty(bssid) || TextUtils.isEmpty(pin)) return false; 417 return doBooleanCommand("WPS_REG " + bssid + " " + pin); 418 } 419 420 public boolean cancelWps() { 421 return doBooleanCommand("WPS_CANCEL"); 422 } 423 424 public boolean setPersistentReconnect(boolean enabled) { 425 int value = (enabled == true) ? 1 : 0; 426 return doBooleanCommand("SET persistent_reconnect " + value); 427 } 428 429 public boolean setDeviceName(String name) { 430 return doBooleanCommand("SET device_name " + name); 431 } 432 433 public boolean setDeviceType(String type) { 434 return doBooleanCommand("SET device_type " + type); 435 } 436 437 public boolean setConfigMethods(String cfg) { 438 return doBooleanCommand("SET config_methods " + cfg); 439 } 440 441 public boolean setManufacturer(String value) { 442 return doBooleanCommand("SET manufacturer " + value); 443 } 444 445 public boolean setModelName(String value) { 446 return doBooleanCommand("SET model_name " + value); 447 } 448 449 public boolean setModelNumber(String value) { 450 return doBooleanCommand("SET model_number " + value); 451 } 452 453 public boolean setSerialNumber(String value) { 454 return doBooleanCommand("SET serial_number " + value); 455 } 456 457 public boolean setP2pSsidPostfix(String postfix) { 458 return doBooleanCommand("SET p2p_ssid_postfix " + postfix); 459 } 460 461 public boolean setP2pGroupIdle(String iface, int time) { 462 return doBooleanCommand("SET interface=" + iface + " p2p_group_idle " + time); 463 } 464 465 public void setPowerSave(boolean enabled) { 466 if (enabled) { 467 doBooleanCommand("SET ps 1"); 468 } else { 469 doBooleanCommand("SET ps 0"); 470 } 471 } 472 473 public boolean setP2pPowerSave(String iface, boolean enabled) { 474 if (enabled) { 475 return doBooleanCommand("P2P_SET interface=" + iface + " ps 1"); 476 } else { 477 return doBooleanCommand("P2P_SET interface=" + iface + " ps 0"); 478 } 479 } 480 481 /** 482 * "sta" prioritizes STA connection over P2P and "p2p" prioritizes 483 * P2P connection over STA 484 */ 485 public boolean setConcurrencyPriority(String s) { 486 return doBooleanCommand("P2P_SET conc_pref " + s); 487 } 488 489 public boolean p2pFind() { 490 return doBooleanCommand("P2P_FIND"); 491 } 492 493 public boolean p2pFind(int timeout) { 494 if (timeout <= 0) { 495 return p2pFind(); 496 } 497 return doBooleanCommand("P2P_FIND " + timeout); 498 } 499 500 public boolean p2pStopFind() { 501 return doBooleanCommand("P2P_STOP_FIND"); 502 } 503 504 public boolean p2pListen() { 505 return doBooleanCommand("P2P_LISTEN"); 506 } 507 508 public boolean p2pListen(int timeout) { 509 if (timeout <= 0) { 510 return p2pListen(); 511 } 512 return doBooleanCommand("P2P_LISTEN " + timeout); 513 } 514 515 public boolean p2pFlush() { 516 return doBooleanCommand("P2P_FLUSH"); 517 } 518 519 /* p2p_connect <peer device address> <pbc|pin|PIN#> [label|display|keypad] 520 [persistent] [join|auth] [go_intent=<0..15>] [freq=<in MHz>] */ 521 public String p2pConnect(WifiP2pConfig config, boolean joinExistingGroup) { 522 if (config == null) return null; 523 List<String> args = new ArrayList<String>(); 524 WpsInfo wps = config.wps; 525 args.add(config.deviceAddress); 526 527 switch (wps.setup) { 528 case WpsInfo.PBC: 529 args.add("pbc"); 530 break; 531 case WpsInfo.DISPLAY: 532 if (TextUtils.isEmpty(wps.pin)) { 533 args.add("pin"); 534 } else { 535 args.add(wps.pin); 536 } 537 args.add("display"); 538 break; 539 case WpsInfo.KEYPAD: 540 args.add(wps.pin); 541 args.add("keypad"); 542 break; 543 case WpsInfo.LABEL: 544 args.add(wps.pin); 545 args.add("label"); 546 default: 547 break; 548 } 549 550 //TODO: Add persist behavior once the supplicant interaction is fixed for both 551 // group and client scenarios 552 /* Persist unless there is an explicit request to not do so*/ 553 //if (config.persist != WifiP2pConfig.Persist.NO) args.add("persistent"); 554 555 if (joinExistingGroup) { 556 args.add("join"); 557 } else { 558 //TODO: This can be adapted based on device plugged in state and 559 //device battery state 560 int groupOwnerIntent = config.groupOwnerIntent; 561 if (groupOwnerIntent < 0 || groupOwnerIntent > 15) { 562 groupOwnerIntent = DEFAULT_GROUP_OWNER_INTENT; 563 } 564 args.add("go_intent=" + groupOwnerIntent); 565 } 566 567 String command = "P2P_CONNECT "; 568 for (String s : args) command += s + " "; 569 570 return doStringCommand(command); 571 } 572 573 public boolean p2pCancelConnect() { 574 return doBooleanCommand("P2P_CANCEL"); 575 } 576 577 public boolean p2pProvisionDiscovery(WifiP2pConfig config) { 578 if (config == null) return false; 579 580 switch (config.wps.setup) { 581 case WpsInfo.PBC: 582 return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " pbc"); 583 case WpsInfo.DISPLAY: 584 //We are doing display, so provision discovery is keypad 585 return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " keypad"); 586 case WpsInfo.KEYPAD: 587 //We are doing keypad, so provision discovery is display 588 return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " display"); 589 default: 590 break; 591 } 592 return false; 593 } 594 595 public boolean p2pGroupAdd() { 596 return doBooleanCommand("P2P_GROUP_ADD"); 597 } 598 599 public boolean p2pGroupRemove(String iface) { 600 if (TextUtils.isEmpty(iface)) return false; 601 return doBooleanCommand("P2P_GROUP_REMOVE " + iface); 602 } 603 604 public boolean p2pReject(String deviceAddress) { 605 return doBooleanCommand("P2P_REJECT " + deviceAddress); 606 } 607 608 /* Invite a peer to a group */ 609 public boolean p2pInvite(WifiP2pGroup group, String deviceAddress) { 610 if (TextUtils.isEmpty(deviceAddress)) return false; 611 612 if (group == null) { 613 return doBooleanCommand("P2P_INVITE peer=" + deviceAddress); 614 } else { 615 return doBooleanCommand("P2P_INVITE group=" + group.getInterface() 616 + " peer=" + deviceAddress + " go_dev_addr=" + group.getOwner().deviceAddress); 617 } 618 } 619 620 /* Reinvoke a persistent connection */ 621 public boolean p2pReinvoke(int netId, String deviceAddress) { 622 if (TextUtils.isEmpty(deviceAddress) || netId < 0) return false; 623 624 return doBooleanCommand("P2P_INVITE persistent=" + netId + " peer=" + deviceAddress); 625 } 626 627 628 public String p2pGetDeviceAddress() { 629 String status = status(); 630 if (status == null) return ""; 631 632 String[] tokens = status.split("\n"); 633 for (String token : tokens) { 634 if (token.startsWith("p2p_device_address=")) { 635 String[] nameValue = token.split("="); 636 if (nameValue.length != 2) break; 637 return nameValue[1]; 638 } 639 } 640 return ""; 641 } 642 643 public int getGroupCapability(String deviceAddress) { 644 int gc = 0; 645 if (TextUtils.isEmpty(deviceAddress)) return gc; 646 String peerInfo = p2pPeer(deviceAddress); 647 if (TextUtils.isEmpty(peerInfo)) return gc; 648 649 String[] tokens = peerInfo.split("\n"); 650 for (String token : tokens) { 651 if (token.startsWith("group_capab=")) { 652 String[] nameValue = token.split("="); 653 if (nameValue.length != 2) break; 654 try { 655 return Integer.decode(nameValue[1]); 656 } catch(NumberFormatException e) { 657 return gc; 658 } 659 } 660 } 661 return gc; 662 } 663 664 public String p2pPeer(String deviceAddress) { 665 return doStringCommand("P2P_PEER " + deviceAddress); 666 } 667 668 public boolean p2pServiceAdd(WifiP2pServiceInfo servInfo) { 669 /* 670 * P2P_SERVICE_ADD bonjour <query hexdump> <RDATA hexdump> 671 * P2P_SERVICE_ADD upnp <version hex> <service> 672 * 673 * e.g) 674 * [Bonjour] 675 * # IP Printing over TCP (PTR) (RDATA=MyPrinter._ipp._tcp.local.) 676 * P2P_SERVICE_ADD bonjour 045f697070c00c000c01 094d795072696e746572c027 677 * # IP Printing over TCP (TXT) (RDATA=txtvers=1,pdl=application/postscript) 678 * P2P_SERVICE_ADD bonjour 096d797072696e746572045f697070c00c001001 679 * 09747874766572733d311a70646c3d6170706c69636174696f6e2f706f7374736372797074 680 * 681 * [UPnP] 682 * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012 683 * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice 684 * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::urn:schemas-upnp 685 * -org:device:InternetGatewayDevice:1 686 * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9322-123456789012::urn:schemas-upnp 687 * -org:service:ContentDirectory:2 688 */ 689 for (String s : servInfo.getSupplicantQueryList()) { 690 String command = "P2P_SERVICE_ADD"; 691 command += (" " + s); 692 if (!doBooleanCommand(command)) { 693 return false; 694 } 695 } 696 return true; 697 } 698 699 public boolean p2pServiceDel(WifiP2pServiceInfo servInfo) { 700 /* 701 * P2P_SERVICE_DEL bonjour <query hexdump> 702 * P2P_SERVICE_DEL upnp <version hex> <service> 703 */ 704 for (String s : servInfo.getSupplicantQueryList()) { 705 String command = "P2P_SERVICE_DEL "; 706 707 String[] data = s.split(" "); 708 if (data.length < 2) { 709 return false; 710 } 711 if ("upnp".equals(data[0])) { 712 command += s; 713 } else if ("bonjour".equals(data[0])) { 714 command += data[0]; 715 command += (" " + data[1]); 716 } else { 717 return false; 718 } 719 if (!doBooleanCommand(command)) { 720 return false; 721 } 722 } 723 return true; 724 } 725 726 public boolean p2pServiceFlush() { 727 return doBooleanCommand("P2P_SERVICE_FLUSH"); 728 } 729 730 public String p2pServDiscReq(String addr, String query) { 731 String command = "P2P_SERV_DISC_REQ"; 732 command += (" " + addr); 733 command += (" " + query); 734 735 return doStringCommand(command); 736 } 737 738 public boolean p2pServDiscCancelReq(String id) { 739 return doBooleanCommand("P2P_SERV_DISC_CANCEL_REQ " + id); 740 } 741 } 742