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