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