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