WifiNative.java revision 633fe60b78665979109d4796ba22430b7b1cd758
1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.net.wifi;
18
19import android.net.wifi.p2p.WifiP2pConfig;
20import android.net.wifi.p2p.WifiP2pGroup;
21import android.net.wifi.p2p.WifiP2pDevice;
22import android.text.TextUtils;
23import android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
24import android.net.wifi.p2p.nsd.WifiP2pServiceRequest;
25import android.util.Log;
26
27import java.io.InputStream;
28import java.lang.Process;
29import java.util.ArrayList;
30import 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 */
41public 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    private boolean mSuspendOptEnabled = false;
53
54    public native static boolean loadDriver();
55
56    public native static boolean isDriverLoaded();
57
58    public native static boolean unloadDriver();
59
60    public native static boolean startSupplicant(boolean p2pSupported);
61
62    /* Sends a kill signal to supplicant. To be used when we have lost connection
63       or when the supplicant is hung */
64    public native static boolean killSupplicant();
65
66    private native boolean connectToSupplicant(String iface);
67
68    private native void closeSupplicantConnection(String iface);
69
70    /**
71     * Wait for the supplicant to send an event, returning the event string.
72     * @return the event string sent by the supplicant.
73     */
74    private native String waitForEvent(String iface);
75
76    private native boolean doBooleanCommand(String iface, String command);
77
78    private native int doIntCommand(String iface, String command);
79
80    private native String doStringCommand(String iface, String command);
81
82    public WifiNative(String iface) {
83        mInterface = iface;
84        mTAG = "WifiNative-" + iface;
85    }
86
87    public boolean connectToSupplicant() {
88        return connectToSupplicant(mInterface);
89    }
90
91    public void closeSupplicantConnection() {
92        closeSupplicantConnection(mInterface);
93    }
94
95    public String waitForEvent() {
96        return waitForEvent(mInterface);
97    }
98
99    private boolean doBooleanCommand(String command) {
100        if (DBG) Log.d(mTAG, "doBoolean: " + command);
101        return doBooleanCommand(mInterface, command);
102    }
103
104    private int doIntCommand(String command) {
105        if (DBG) Log.d(mTAG, "doInt: " + command);
106        return doIntCommand(mInterface, command);
107    }
108
109    private String doStringCommand(String command) {
110        if (DBG) Log.d(mTAG, "doString: " + command);
111        return doStringCommand(mInterface, command);
112    }
113
114    public boolean ping() {
115        String pong = doStringCommand("PING");
116        return (pong != null && pong.equals("PONG"));
117    }
118
119    public boolean scan() {
120       return doBooleanCommand("SCAN");
121    }
122
123    public boolean setScanMode(boolean setActive) {
124        if (setActive) {
125            return doBooleanCommand("DRIVER SCAN-ACTIVE");
126        } else {
127            return doBooleanCommand("DRIVER SCAN-PASSIVE");
128        }
129    }
130
131    /* Does a graceful shutdown of supplicant. Is a common stop function for both p2p and sta.
132     *
133     * Note that underneath we use a harsh-sounding "terminate" supplicant command
134     * for a graceful stop and a mild-sounding "stop" interface
135     * to kill the process
136     */
137    public boolean stopSupplicant() {
138        return doBooleanCommand("TERMINATE");
139    }
140
141    public String listNetworks() {
142        return doStringCommand("LIST_NETWORKS");
143    }
144
145    public int addNetwork() {
146        return doIntCommand("ADD_NETWORK");
147    }
148
149    public boolean setNetworkVariable(int netId, String name, String value) {
150        if (TextUtils.isEmpty(name) || TextUtils.isEmpty(value)) return false;
151        return doBooleanCommand("SET_NETWORK " + netId + " " + name + " " + value);
152    }
153
154    public String getNetworkVariable(int netId, String name) {
155        if (TextUtils.isEmpty(name)) return null;
156        return doStringCommand("GET_NETWORK " + netId + " " + name);
157    }
158
159    public boolean removeNetwork(int netId) {
160        return doBooleanCommand("REMOVE_NETWORK " + netId);
161    }
162
163    public boolean enableNetwork(int netId, boolean disableOthers) {
164        if (disableOthers) {
165            return doBooleanCommand("SELECT_NETWORK " + netId);
166        } else {
167            return doBooleanCommand("ENABLE_NETWORK " + netId);
168        }
169    }
170
171    public boolean disableNetwork(int netId) {
172        return doBooleanCommand("DISABLE_NETWORK " + netId);
173    }
174
175    public boolean reconnect() {
176        return doBooleanCommand("RECONNECT");
177    }
178
179    public boolean reassociate() {
180        return doBooleanCommand("REASSOCIATE");
181    }
182
183    public boolean disconnect() {
184        return doBooleanCommand("DISCONNECT");
185    }
186
187    public String status() {
188        return doStringCommand("STATUS");
189    }
190
191    public String getMacAddress() {
192        //Macaddr = XX.XX.XX.XX.XX.XX
193        String ret = doStringCommand("DRIVER MACADDR");
194        if (!TextUtils.isEmpty(ret)) {
195            String[] tokens = ret.split(" = ");
196            if (tokens.length == 2) return tokens[1];
197        }
198        return null;
199    }
200
201    /**
202     * Format of results:
203     * =================
204     * bssid=68:7f:74:d7:1b:6e
205     * freq=2412
206     * level=-43
207     * tsf=1344621975160944
208     * age=2623
209     * flags=[WPA2-PSK-CCMP][WPS][ESS]
210     * ssid=zubyb
211     *
212     * RANGE=ALL gets all scan results
213     * MASK=<N> see wpa_supplicant/src/common/wpa_ctrl.h for details
214     */
215    public String scanResults() {
216        return doStringCommand("BSS RANGE=ALL MASK=0x1986");
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        // Make sure we never write out a value for AP_SCAN other than 1
336        return doBooleanCommand("AP_SCAN 1") && doBooleanCommand("SAVE_CONFIG");
337    }
338
339    public boolean setScanResultHandling(int mode) {
340        return doBooleanCommand("AP_SCAN " + mode);
341    }
342
343    public boolean addToBlacklist(String bssid) {
344        if (TextUtils.isEmpty(bssid)) return false;
345        return doBooleanCommand("BLACKLIST " + bssid);
346    }
347
348    public boolean clearBlacklist() {
349        return doBooleanCommand("BLACKLIST clear");
350    }
351
352    public boolean setSuspendOptimizations(boolean enabled) {
353        if (mSuspendOptEnabled == enabled) return true;
354        mSuspendOptEnabled = enabled;
355        if (enabled) {
356            return doBooleanCommand("DRIVER SETSUSPENDMODE 1");
357        } else {
358            return doBooleanCommand("DRIVER SETSUSPENDMODE 0");
359        }
360    }
361
362    public boolean setCountryCode(String countryCode) {
363        return doBooleanCommand("DRIVER COUNTRY " + countryCode);
364    }
365
366    public void enableBackgroundScan(boolean enable) {
367        if (enable) {
368            doBooleanCommand("SET pno 1");
369        } else {
370            doBooleanCommand("SET pno 0");
371        }
372    }
373
374    public void setScanInterval(int scanInterval) {
375        doBooleanCommand("SCAN_INTERVAL " + scanInterval);
376    }
377
378    /** Example output:
379     * RSSI=-65
380     * LINKSPEED=48
381     * NOISE=9999
382     * FREQUENCY=0
383     */
384    public String signalPoll() {
385        return doStringCommand("SIGNAL_POLL");
386    }
387
388    /** Example outout:
389     * TXGOOD=396
390     * TXBAD=1
391     */
392    public String pktcntPoll() {
393        return doStringCommand("PKTCNT_POLL");
394    }
395
396    public boolean startWpsPbc(String bssid) {
397        if (TextUtils.isEmpty(bssid)) {
398            return doBooleanCommand("WPS_PBC");
399        } else {
400            return doBooleanCommand("WPS_PBC " + bssid);
401        }
402    }
403
404    public boolean startWpsPbc(String iface, String bssid) {
405        if (TextUtils.isEmpty(bssid)) {
406            return doBooleanCommand("WPS_PBC interface=" + iface);
407        } else {
408            return doBooleanCommand("WPS_PBC interface=" + iface + " " + bssid);
409        }
410    }
411
412    public boolean startWpsPinKeypad(String pin) {
413        if (TextUtils.isEmpty(pin)) return false;
414        return doBooleanCommand("WPS_PIN any " + pin);
415    }
416
417    public boolean startWpsPinKeypad(String iface, String pin) {
418        if (TextUtils.isEmpty(pin)) return false;
419        return doBooleanCommand("WPS_PIN interface=" + iface + " any " + pin);
420    }
421
422
423    public String startWpsPinDisplay(String bssid) {
424        if (TextUtils.isEmpty(bssid)) {
425            return doStringCommand("WPS_PIN any");
426        } else {
427            return doStringCommand("WPS_PIN " + bssid);
428        }
429    }
430
431    public String startWpsPinDisplay(String iface, String bssid) {
432        if (TextUtils.isEmpty(bssid)) {
433            return doStringCommand("WPS_PIN interface=" + iface + " any");
434        } else {
435            return doStringCommand("WPS_PIN interface=" + iface + " " + bssid);
436        }
437    }
438
439    /* Configures an access point connection */
440    public boolean startWpsRegistrar(String bssid, String pin) {
441        if (TextUtils.isEmpty(bssid) || TextUtils.isEmpty(pin)) return false;
442        return doBooleanCommand("WPS_REG " + bssid + " " + pin);
443    }
444
445    public boolean cancelWps() {
446        return doBooleanCommand("WPS_CANCEL");
447    }
448
449    public boolean setPersistentReconnect(boolean enabled) {
450        int value = (enabled == true) ? 1 : 0;
451        return doBooleanCommand("SET persistent_reconnect " + value);
452    }
453
454    public boolean setDeviceName(String name) {
455        return doBooleanCommand("SET device_name " + name);
456    }
457
458    public boolean setDeviceType(String type) {
459        return doBooleanCommand("SET device_type " + type);
460    }
461
462    public boolean setConfigMethods(String cfg) {
463        return doBooleanCommand("SET config_methods " + cfg);
464    }
465
466    public boolean setManufacturer(String value) {
467        return doBooleanCommand("SET manufacturer " + value);
468    }
469
470    public boolean setModelName(String value) {
471        return doBooleanCommand("SET model_name " + value);
472    }
473
474    public boolean setModelNumber(String value) {
475        return doBooleanCommand("SET model_number " + value);
476    }
477
478    public boolean setSerialNumber(String value) {
479        return doBooleanCommand("SET serial_number " + value);
480    }
481
482    public boolean setP2pSsidPostfix(String postfix) {
483        return doBooleanCommand("SET p2p_ssid_postfix " + postfix);
484    }
485
486    public boolean setP2pGroupIdle(String iface, int time) {
487        return doBooleanCommand("SET interface=" + iface + " p2p_group_idle " + time);
488    }
489
490    public void setPowerSave(boolean enabled) {
491        if (enabled) {
492            doBooleanCommand("SET ps 1");
493        } else {
494            doBooleanCommand("SET ps 0");
495        }
496    }
497
498    public boolean setP2pPowerSave(String iface, boolean enabled) {
499        if (enabled) {
500            return doBooleanCommand("P2P_SET interface=" + iface + " ps 1");
501        } else {
502            return doBooleanCommand("P2P_SET interface=" + iface + " ps 0");
503        }
504    }
505
506    public boolean setWfdEnable(boolean enable) {
507        return doBooleanCommand("SET wifi_display " + (enable ? "1" : "0"));
508    }
509
510    public boolean setWfdDeviceInfo(String hex) {
511        return doBooleanCommand("WFD_SUBELEM_SET 0 " + hex);
512    }
513
514    /**
515     * "sta" prioritizes STA connection over P2P and "p2p" prioritizes
516     * P2P connection over STA
517     */
518    public boolean setConcurrencyPriority(String s) {
519        return doBooleanCommand("P2P_SET conc_pref " + s);
520    }
521
522    public boolean p2pFind() {
523        return doBooleanCommand("P2P_FIND");
524    }
525
526    public boolean p2pFind(int timeout) {
527        if (timeout <= 0) {
528            return p2pFind();
529        }
530        return doBooleanCommand("P2P_FIND " + timeout);
531    }
532
533    public boolean p2pStopFind() {
534       return doBooleanCommand("P2P_STOP_FIND");
535    }
536
537    public boolean p2pListen() {
538        return doBooleanCommand("P2P_LISTEN");
539    }
540
541    public boolean p2pListen(int timeout) {
542        if (timeout <= 0) {
543            return p2pListen();
544        }
545        return doBooleanCommand("P2P_LISTEN " + timeout);
546    }
547
548    public boolean p2pFlush() {
549        return doBooleanCommand("P2P_FLUSH");
550    }
551
552    /* p2p_connect <peer device address> <pbc|pin|PIN#> [label|display|keypad]
553        [persistent] [join|auth] [go_intent=<0..15>] [freq=<in MHz>] */
554    public String p2pConnect(WifiP2pConfig config, boolean joinExistingGroup) {
555        if (config == null) return null;
556        List<String> args = new ArrayList<String>();
557        WpsInfo wps = config.wps;
558        args.add(config.deviceAddress);
559
560        switch (wps.setup) {
561            case WpsInfo.PBC:
562                args.add("pbc");
563                break;
564            case WpsInfo.DISPLAY:
565                if (TextUtils.isEmpty(wps.pin)) {
566                    args.add("pin");
567                } else {
568                    args.add(wps.pin);
569                }
570                args.add("display");
571                break;
572            case WpsInfo.KEYPAD:
573                args.add(wps.pin);
574                args.add("keypad");
575                break;
576            case WpsInfo.LABEL:
577                args.add(wps.pin);
578                args.add("label");
579            default:
580                break;
581        }
582
583        if (config.netId == WifiP2pGroup.PERSISTENT_NET_ID) {
584            args.add("persistent");
585        }
586
587        if (joinExistingGroup) {
588            args.add("join");
589        } else {
590            //TODO: This can be adapted based on device plugged in state and
591            //device battery state
592            int groupOwnerIntent = config.groupOwnerIntent;
593            if (groupOwnerIntent < 0 || groupOwnerIntent > 15) {
594                groupOwnerIntent = DEFAULT_GROUP_OWNER_INTENT;
595            }
596            args.add("go_intent=" + groupOwnerIntent);
597        }
598
599        String command = "P2P_CONNECT ";
600        for (String s : args) command += s + " ";
601
602        return doStringCommand(command);
603    }
604
605    public boolean p2pCancelConnect() {
606        return doBooleanCommand("P2P_CANCEL");
607    }
608
609    public boolean p2pProvisionDiscovery(WifiP2pConfig config) {
610        if (config == null) return false;
611
612        switch (config.wps.setup) {
613            case WpsInfo.PBC:
614                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " pbc");
615            case WpsInfo.DISPLAY:
616                //We are doing display, so provision discovery is keypad
617                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " keypad");
618            case WpsInfo.KEYPAD:
619                //We are doing keypad, so provision discovery is display
620                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " display");
621            default:
622                break;
623        }
624        return false;
625    }
626
627    public boolean p2pGroupAdd(boolean persistent) {
628        if (persistent) {
629            return doBooleanCommand("P2P_GROUP_ADD persistent");
630        }
631        return doBooleanCommand("P2P_GROUP_ADD");
632    }
633
634    public boolean p2pGroupAdd(int netId) {
635        return doBooleanCommand("P2P_GROUP_ADD persistent=" + netId);
636    }
637
638    public boolean p2pGroupRemove(String iface) {
639        if (TextUtils.isEmpty(iface)) return false;
640        return doBooleanCommand("P2P_GROUP_REMOVE " + iface);
641    }
642
643    public boolean p2pReject(String deviceAddress) {
644        return doBooleanCommand("P2P_REJECT " + deviceAddress);
645    }
646
647    /* Invite a peer to a group */
648    public boolean p2pInvite(WifiP2pGroup group, String deviceAddress) {
649        if (TextUtils.isEmpty(deviceAddress)) return false;
650
651        if (group == null) {
652            return doBooleanCommand("P2P_INVITE peer=" + deviceAddress);
653        } else {
654            return doBooleanCommand("P2P_INVITE group=" + group.getInterface()
655                    + " peer=" + deviceAddress + " go_dev_addr=" + group.getOwner().deviceAddress);
656        }
657    }
658
659    /* Reinvoke a persistent connection */
660    public boolean p2pReinvoke(int netId, String deviceAddress) {
661        if (TextUtils.isEmpty(deviceAddress) || netId < 0) return false;
662
663        return doBooleanCommand("P2P_INVITE persistent=" + netId + " peer=" + deviceAddress);
664    }
665
666    public String p2pGetSsid(String deviceAddress) {
667        return p2pGetParam(deviceAddress, "oper_ssid");
668    }
669
670    public String p2pGetDeviceAddress() {
671        String status = status();
672        if (status == null) return "";
673
674        String[] tokens = status.split("\n");
675        for (String token : tokens) {
676            if (token.startsWith("p2p_device_address=")) {
677                String[] nameValue = token.split("=");
678                if (nameValue.length != 2) break;
679                return nameValue[1];
680            }
681        }
682        return "";
683    }
684
685    public int getGroupCapability(String deviceAddress) {
686        int gc = 0;
687        if (TextUtils.isEmpty(deviceAddress)) return gc;
688        String peerInfo = p2pPeer(deviceAddress);
689        if (TextUtils.isEmpty(peerInfo)) return gc;
690
691        String[] tokens = peerInfo.split("\n");
692        for (String token : tokens) {
693            if (token.startsWith("group_capab=")) {
694                String[] nameValue = token.split("=");
695                if (nameValue.length != 2) break;
696                try {
697                    return Integer.decode(nameValue[1]);
698                } catch(NumberFormatException e) {
699                    return gc;
700                }
701            }
702        }
703        return gc;
704    }
705
706    public String p2pPeer(String deviceAddress) {
707        return doStringCommand("P2P_PEER " + deviceAddress);
708    }
709
710    private String p2pGetParam(String deviceAddress, String key) {
711        if (deviceAddress == null) return null;
712
713        String peerInfo = p2pPeer(deviceAddress);
714        if (peerInfo == null) return null;
715        String[] tokens= peerInfo.split("\n");
716
717        key += "=";
718        for (String token : tokens) {
719            if (token.startsWith(key)) {
720                String[] nameValue = token.split("=");
721                if (nameValue.length != 2) break;
722                return nameValue[1];
723            }
724        }
725        return null;
726    }
727
728    public boolean p2pServiceAdd(WifiP2pServiceInfo servInfo) {
729        /*
730         * P2P_SERVICE_ADD bonjour <query hexdump> <RDATA hexdump>
731         * P2P_SERVICE_ADD upnp <version hex> <service>
732         *
733         * e.g)
734         * [Bonjour]
735         * # IP Printing over TCP (PTR) (RDATA=MyPrinter._ipp._tcp.local.)
736         * P2P_SERVICE_ADD bonjour 045f697070c00c000c01 094d795072696e746572c027
737         * # IP Printing over TCP (TXT) (RDATA=txtvers=1,pdl=application/postscript)
738         * P2P_SERVICE_ADD bonjour 096d797072696e746572045f697070c00c001001
739         *  09747874766572733d311a70646c3d6170706c69636174696f6e2f706f7374736372797074
740         *
741         * [UPnP]
742         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012
743         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice
744         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::urn:schemas-upnp
745         * -org:device:InternetGatewayDevice:1
746         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9322-123456789012::urn:schemas-upnp
747         * -org:service:ContentDirectory:2
748         */
749        for (String s : servInfo.getSupplicantQueryList()) {
750            String command = "P2P_SERVICE_ADD";
751            command += (" " + s);
752            if (!doBooleanCommand(command)) {
753                return false;
754            }
755        }
756        return true;
757    }
758
759    public boolean p2pServiceDel(WifiP2pServiceInfo servInfo) {
760        /*
761         * P2P_SERVICE_DEL bonjour <query hexdump>
762         * P2P_SERVICE_DEL upnp <version hex> <service>
763         */
764        for (String s : servInfo.getSupplicantQueryList()) {
765            String command = "P2P_SERVICE_DEL ";
766
767            String[] data = s.split(" ");
768            if (data.length < 2) {
769                return false;
770            }
771            if ("upnp".equals(data[0])) {
772                command += s;
773            } else if ("bonjour".equals(data[0])) {
774                command += data[0];
775                command += (" " + data[1]);
776            } else {
777                return false;
778            }
779            if (!doBooleanCommand(command)) {
780                return false;
781            }
782        }
783        return true;
784    }
785
786    public boolean p2pServiceFlush() {
787        return doBooleanCommand("P2P_SERVICE_FLUSH");
788    }
789
790    public String p2pServDiscReq(String addr, String query) {
791        String command = "P2P_SERV_DISC_REQ";
792        command += (" " + addr);
793        command += (" " + query);
794
795        return doStringCommand(command);
796    }
797
798    public boolean p2pServDiscCancelReq(String id) {
799        return doBooleanCommand("P2P_SERV_DISC_CANCEL_REQ " + id);
800    }
801}
802