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