Home | History | Annotate | Download | only in wifi
      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