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.Log;
     24 
     25 import java.util.ArrayList;
     26 import java.util.List;
     27 
     28 /**
     29  * Native calls for bring up/shut down of the supplicant daemon and for
     30  * sending requests to the supplicant daemon
     31  *
     32  * waitForEvent() is called on the monitor thread for events. All other methods
     33  * must be serialized from the framework.
     34  *
     35  * {@hide}
     36  */
     37 public class WifiNative {
     38 
     39     private static final boolean DBG = false;
     40     private final String mTAG;
     41     private static final int DEFAULT_GROUP_OWNER_INTENT     = 6;
     42 
     43     static final int BLUETOOTH_COEXISTENCE_MODE_ENABLED     = 0;
     44     static final int BLUETOOTH_COEXISTENCE_MODE_DISABLED    = 1;
     45     static final int BLUETOOTH_COEXISTENCE_MODE_SENSE       = 2;
     46 
     47     static final int SCAN_WITHOUT_CONNECTION_SETUP          = 1;
     48     static final int SCAN_WITH_CONNECTION_SETUP             = 2;
     49 
     50     String mInterface = "";
     51     private boolean mSuspendOptEnabled = false;
     52 
     53     public native static boolean loadDriver();
     54 
     55     public native static boolean isDriverLoaded();
     56 
     57     public native static boolean unloadDriver();
     58 
     59     public native static boolean startSupplicant(boolean p2pSupported);
     60 
     61     /* Sends a kill signal to supplicant. To be used when we have lost connection
     62        or when the supplicant is hung */
     63     public native static boolean killSupplicant(boolean p2pSupported);
     64 
     65     private native boolean connectToSupplicant(String iface);
     66 
     67     private native void closeSupplicantConnection(String iface);
     68 
     69     /**
     70      * Wait for the supplicant to send an event, returning the event string.
     71      * @return the event string sent by the supplicant.
     72      */
     73     private native String waitForEvent(String iface);
     74 
     75     private native boolean doBooleanCommand(String iface, String command);
     76 
     77     private native int doIntCommand(String iface, String command);
     78 
     79     private native String doStringCommand(String iface, String command);
     80 
     81     public WifiNative(String iface) {
     82         mInterface = iface;
     83         mTAG = "WifiNative-" + iface;
     84     }
     85 
     86     public boolean connectToSupplicant() {
     87         return connectToSupplicant(mInterface);
     88     }
     89 
     90     public void closeSupplicantConnection() {
     91         closeSupplicantConnection(mInterface);
     92     }
     93 
     94     public String waitForEvent() {
     95         return waitForEvent(mInterface);
     96     }
     97 
     98     private boolean doBooleanCommand(String command) {
     99         if (DBG) Log.d(mTAG, "doBoolean: " + command);
    100         return doBooleanCommand(mInterface, command);
    101     }
    102 
    103     private int doIntCommand(String command) {
    104         if (DBG) Log.d(mTAG, "doInt: " + command);
    105         return doIntCommand(mInterface, command);
    106     }
    107 
    108     private String doStringCommand(String command) {
    109         if (DBG) Log.d(mTAG, "doString: " + command);
    110         return doStringCommand(mInterface, command);
    111     }
    112 
    113     public boolean ping() {
    114         String pong = doStringCommand("PING");
    115         return (pong != null && pong.equals("PONG"));
    116     }
    117 
    118     public boolean scan(int type) {
    119         if (type == SCAN_WITHOUT_CONNECTION_SETUP) {
    120             return doBooleanCommand("SCAN TYPE=ONLY");
    121         } else if (type == SCAN_WITH_CONNECTION_SETUP) {
    122             return doBooleanCommand("SCAN");
    123         } else {
    124             throw new IllegalArgumentException("Invalid scan type");
    125         }
    126     }
    127 
    128     /* Does a graceful shutdown of supplicant. Is a common stop function for both p2p and sta.
    129      *
    130      * Note that underneath we use a harsh-sounding "terminate" supplicant command
    131      * for a graceful stop and a mild-sounding "stop" interface
    132      * to kill the process
    133      */
    134     public boolean stopSupplicant() {
    135         return doBooleanCommand("TERMINATE");
    136     }
    137 
    138     public String listNetworks() {
    139         return doStringCommand("LIST_NETWORKS");
    140     }
    141 
    142     public int addNetwork() {
    143         return doIntCommand("ADD_NETWORK");
    144     }
    145 
    146     public boolean setNetworkVariable(int netId, String name, String value) {
    147         if (TextUtils.isEmpty(name) || TextUtils.isEmpty(value)) return false;
    148         return doBooleanCommand("SET_NETWORK " + netId + " " + name + " " + value);
    149     }
    150 
    151     public String getNetworkVariable(int netId, String name) {
    152         if (TextUtils.isEmpty(name)) return null;
    153         return doStringCommand("GET_NETWORK " + netId + " " + name);
    154     }
    155 
    156     public boolean removeNetwork(int netId) {
    157         return doBooleanCommand("REMOVE_NETWORK " + netId);
    158     }
    159 
    160     public boolean enableNetwork(int netId, boolean disableOthers) {
    161         if (disableOthers) {
    162             return doBooleanCommand("SELECT_NETWORK " + netId);
    163         } else {
    164             return doBooleanCommand("ENABLE_NETWORK " + netId);
    165         }
    166     }
    167 
    168     public boolean disableNetwork(int netId) {
    169         return doBooleanCommand("DISABLE_NETWORK " + netId);
    170     }
    171 
    172     public boolean reconnect() {
    173         return doBooleanCommand("RECONNECT");
    174     }
    175 
    176     public boolean reassociate() {
    177         return doBooleanCommand("REASSOCIATE");
    178     }
    179 
    180     public boolean disconnect() {
    181         return doBooleanCommand("DISCONNECT");
    182     }
    183 
    184     public String status() {
    185         return doStringCommand("STATUS");
    186     }
    187 
    188     public String getMacAddress() {
    189         //Macaddr = XX.XX.XX.XX.XX.XX
    190         String ret = doStringCommand("DRIVER MACADDR");
    191         if (!TextUtils.isEmpty(ret)) {
    192             String[] tokens = ret.split(" = ");
    193             if (tokens.length == 2) return tokens[1];
    194         }
    195         return null;
    196     }
    197 
    198     /**
    199      * Format of results:
    200      * =================
    201      * id=1
    202      * bssid=68:7f:74:d7:1b:6e
    203      * freq=2412
    204      * level=-43
    205      * tsf=1344621975160944
    206      * age=2623
    207      * flags=[WPA2-PSK-CCMP][WPS][ESS]
    208      * ssid=zubyb
    209      * ====
    210      *
    211      * RANGE=ALL gets all scan results
    212      * RANGE=ID- gets results from ID
    213      * MASK=<N> see wpa_supplicant/src/common/wpa_ctrl.h for details
    214      */
    215     public String scanResults(int sid) {
    216         return doStringCommand("BSS RANGE=" + sid + "- MASK=0x21987");
    217     }
    218 
    219     public boolean startDriver() {
    220         return doBooleanCommand("DRIVER START");
    221     }
    222 
    223     public boolean stopDriver() {
    224         return doBooleanCommand("DRIVER STOP");
    225     }
    226 
    227 
    228     /**
    229      * Start filtering out Multicast V4 packets
    230      * @return {@code true} if the operation succeeded, {@code false} otherwise
    231      *
    232      * Multicast filtering rules work as follows:
    233      *
    234      * The driver can filter multicast (v4 and/or v6) and broadcast packets when in
    235      * a power optimized mode (typically when screen goes off).
    236      *
    237      * In order to prevent the driver from filtering the multicast/broadcast packets, we have to
    238      * add a DRIVER RXFILTER-ADD rule followed by DRIVER RXFILTER-START to make the rule effective
    239      *
    240      * DRIVER RXFILTER-ADD Num
    241      *   where Num = 0 - Unicast, 1 - Broadcast, 2 - Mutil4 or 3 - Multi6
    242      *
    243      * and DRIVER RXFILTER-START
    244      * In order to stop the usage of these rules, we do
    245      *
    246      * DRIVER RXFILTER-STOP
    247      * DRIVER RXFILTER-REMOVE Num
    248      *   where Num is as described for RXFILTER-ADD
    249      *
    250      * The  SETSUSPENDOPT driver command overrides the filtering rules
    251      */
    252     public boolean startFilteringMulticastV4Packets() {
    253         return doBooleanCommand("DRIVER RXFILTER-STOP")
    254             && doBooleanCommand("DRIVER RXFILTER-REMOVE 2")
    255             && doBooleanCommand("DRIVER RXFILTER-START");
    256     }
    257 
    258     /**
    259      * Stop filtering out Multicast V4 packets.
    260      * @return {@code true} if the operation succeeded, {@code false} otherwise
    261      */
    262     public boolean stopFilteringMulticastV4Packets() {
    263         return doBooleanCommand("DRIVER RXFILTER-STOP")
    264             && doBooleanCommand("DRIVER RXFILTER-ADD 2")
    265             && doBooleanCommand("DRIVER RXFILTER-START");
    266     }
    267 
    268     /**
    269      * Start filtering out Multicast V6 packets
    270      * @return {@code true} if the operation succeeded, {@code false} otherwise
    271      */
    272     public boolean startFilteringMulticastV6Packets() {
    273         return doBooleanCommand("DRIVER RXFILTER-STOP")
    274             && doBooleanCommand("DRIVER RXFILTER-REMOVE 3")
    275             && doBooleanCommand("DRIVER RXFILTER-START");
    276     }
    277 
    278     /**
    279      * Stop filtering out Multicast V6 packets.
    280      * @return {@code true} if the operation succeeded, {@code false} otherwise
    281      */
    282     public boolean stopFilteringMulticastV6Packets() {
    283         return doBooleanCommand("DRIVER RXFILTER-STOP")
    284             && doBooleanCommand("DRIVER RXFILTER-ADD 3")
    285             && doBooleanCommand("DRIVER RXFILTER-START");
    286     }
    287 
    288     public int getBand() {
    289        String ret = doStringCommand("DRIVER GETBAND");
    290         if (!TextUtils.isEmpty(ret)) {
    291             //reply is "BAND X" where X is the band
    292             String[] tokens = ret.split(" ");
    293             try {
    294                 if (tokens.length == 2) return Integer.parseInt(tokens[1]);
    295             } catch (NumberFormatException e) {
    296                 return -1;
    297             }
    298         }
    299         return -1;
    300     }
    301 
    302     public boolean setBand(int band) {
    303         return doBooleanCommand("DRIVER SETBAND " + band);
    304     }
    305 
    306    /**
    307      * Sets the bluetooth coexistence mode.
    308      *
    309      * @param mode One of {@link #BLUETOOTH_COEXISTENCE_MODE_DISABLED},
    310      *            {@link #BLUETOOTH_COEXISTENCE_MODE_ENABLED}, or
    311      *            {@link #BLUETOOTH_COEXISTENCE_MODE_SENSE}.
    312      * @return Whether the mode was successfully set.
    313      */
    314     public boolean setBluetoothCoexistenceMode(int mode) {
    315         return doBooleanCommand("DRIVER BTCOEXMODE " + mode);
    316     }
    317 
    318     /**
    319      * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
    320      * some of the low-level scan parameters used by the driver are changed to
    321      * reduce interference with A2DP streaming.
    322      *
    323      * @param isSet whether to enable or disable this mode
    324      * @return {@code true} if the command succeeded, {@code false} otherwise.
    325      */
    326     public boolean setBluetoothCoexistenceScanMode(boolean setCoexScanMode) {
    327         if (setCoexScanMode) {
    328             return doBooleanCommand("DRIVER BTCOEXSCAN-START");
    329         } else {
    330             return doBooleanCommand("DRIVER BTCOEXSCAN-STOP");
    331         }
    332     }
    333 
    334     public boolean saveConfig() {
    335         return doBooleanCommand("SAVE_CONFIG");
    336     }
    337 
    338     public boolean addToBlacklist(String bssid) {
    339         if (TextUtils.isEmpty(bssid)) return false;
    340         return doBooleanCommand("BLACKLIST " + bssid);
    341     }
    342 
    343     public boolean clearBlacklist() {
    344         return doBooleanCommand("BLACKLIST clear");
    345     }
    346 
    347     public boolean setSuspendOptimizations(boolean enabled) {
    348         if (mSuspendOptEnabled == enabled) return true;
    349         mSuspendOptEnabled = enabled;
    350         if (enabled) {
    351             return doBooleanCommand("DRIVER SETSUSPENDMODE 1");
    352         } else {
    353             return doBooleanCommand("DRIVER SETSUSPENDMODE 0");
    354         }
    355     }
    356 
    357     public boolean setCountryCode(String countryCode) {
    358         return doBooleanCommand("DRIVER COUNTRY " + countryCode);
    359     }
    360 
    361     public void enableBackgroundScan(boolean enable) {
    362         if (enable) {
    363             doBooleanCommand("SET pno 1");
    364         } else {
    365             doBooleanCommand("SET pno 0");
    366         }
    367     }
    368 
    369     public void setScanInterval(int scanInterval) {
    370         doBooleanCommand("SCAN_INTERVAL " + scanInterval);
    371     }
    372 
    373     /** Example output:
    374      * RSSI=-65
    375      * LINKSPEED=48
    376      * NOISE=9999
    377      * FREQUENCY=0
    378      */
    379     public String signalPoll() {
    380         return doStringCommand("SIGNAL_POLL");
    381     }
    382 
    383     /** Example outout:
    384      * TXGOOD=396
    385      * TXBAD=1
    386      */
    387     public String pktcntPoll() {
    388         return doStringCommand("PKTCNT_POLL");
    389     }
    390 
    391     public boolean startWpsPbc(String bssid) {
    392         if (TextUtils.isEmpty(bssid)) {
    393             return doBooleanCommand("WPS_PBC");
    394         } else {
    395             return doBooleanCommand("WPS_PBC " + bssid);
    396         }
    397     }
    398 
    399     public boolean startWpsPbc(String iface, String bssid) {
    400         if (TextUtils.isEmpty(bssid)) {
    401             return doBooleanCommand("WPS_PBC interface=" + iface);
    402         } else {
    403             return doBooleanCommand("WPS_PBC interface=" + iface + " " + bssid);
    404         }
    405     }
    406 
    407     public boolean startWpsPinKeypad(String pin) {
    408         if (TextUtils.isEmpty(pin)) return false;
    409         return doBooleanCommand("WPS_PIN any " + pin);
    410     }
    411 
    412     public boolean startWpsPinKeypad(String iface, String pin) {
    413         if (TextUtils.isEmpty(pin)) return false;
    414         return doBooleanCommand("WPS_PIN interface=" + iface + " any " + pin);
    415     }
    416 
    417 
    418     public String startWpsPinDisplay(String bssid) {
    419         if (TextUtils.isEmpty(bssid)) {
    420             return doStringCommand("WPS_PIN any");
    421         } else {
    422             return doStringCommand("WPS_PIN " + bssid);
    423         }
    424     }
    425 
    426     public String startWpsPinDisplay(String iface, String bssid) {
    427         if (TextUtils.isEmpty(bssid)) {
    428             return doStringCommand("WPS_PIN interface=" + iface + " any");
    429         } else {
    430             return doStringCommand("WPS_PIN interface=" + iface + " " + bssid);
    431         }
    432     }
    433 
    434     /* Configures an access point connection */
    435     public boolean startWpsRegistrar(String bssid, String pin) {
    436         if (TextUtils.isEmpty(bssid) || TextUtils.isEmpty(pin)) return false;
    437         return doBooleanCommand("WPS_REG " + bssid + " " + pin);
    438     }
    439 
    440     public boolean cancelWps() {
    441         return doBooleanCommand("WPS_CANCEL");
    442     }
    443 
    444     public boolean setPersistentReconnect(boolean enabled) {
    445         int value = (enabled == true) ? 1 : 0;
    446         return doBooleanCommand("SET persistent_reconnect " + value);
    447     }
    448 
    449     public boolean setDeviceName(String name) {
    450         return doBooleanCommand("SET device_name " + name);
    451     }
    452 
    453     public boolean setDeviceType(String type) {
    454         return doBooleanCommand("SET device_type " + type);
    455     }
    456 
    457     public boolean setConfigMethods(String cfg) {
    458         return doBooleanCommand("SET config_methods " + cfg);
    459     }
    460 
    461     public boolean setManufacturer(String value) {
    462         return doBooleanCommand("SET manufacturer " + value);
    463     }
    464 
    465     public boolean setModelName(String value) {
    466         return doBooleanCommand("SET model_name " + value);
    467     }
    468 
    469     public boolean setModelNumber(String value) {
    470         return doBooleanCommand("SET model_number " + value);
    471     }
    472 
    473     public boolean setSerialNumber(String value) {
    474         return doBooleanCommand("SET serial_number " + value);
    475     }
    476 
    477     public boolean setP2pSsidPostfix(String postfix) {
    478         return doBooleanCommand("SET p2p_ssid_postfix " + postfix);
    479     }
    480 
    481     public boolean setP2pGroupIdle(String iface, int time) {
    482         return doBooleanCommand("SET interface=" + iface + " p2p_group_idle " + time);
    483     }
    484 
    485     public void setPowerSave(boolean enabled) {
    486         if (enabled) {
    487             doBooleanCommand("SET ps 1");
    488         } else {
    489             doBooleanCommand("SET ps 0");
    490         }
    491     }
    492 
    493     public boolean setP2pPowerSave(String iface, boolean enabled) {
    494         if (enabled) {
    495             return doBooleanCommand("P2P_SET interface=" + iface + " ps 1");
    496         } else {
    497             return doBooleanCommand("P2P_SET interface=" + iface + " ps 0");
    498         }
    499     }
    500 
    501     public boolean setWfdEnable(boolean enable) {
    502         return doBooleanCommand("SET wifi_display " + (enable ? "1" : "0"));
    503     }
    504 
    505     public boolean setWfdDeviceInfo(String hex) {
    506         return doBooleanCommand("WFD_SUBELEM_SET 0 " + hex);
    507     }
    508 
    509     /**
    510      * "sta" prioritizes STA connection over P2P and "p2p" prioritizes
    511      * P2P connection over STA
    512      */
    513     public boolean setConcurrencyPriority(String s) {
    514         return doBooleanCommand("P2P_SET conc_pref " + s);
    515     }
    516 
    517     public boolean p2pFind() {
    518         return doBooleanCommand("P2P_FIND");
    519     }
    520 
    521     public boolean p2pFind(int timeout) {
    522         if (timeout <= 0) {
    523             return p2pFind();
    524         }
    525         return doBooleanCommand("P2P_FIND " + timeout);
    526     }
    527 
    528     public boolean p2pStopFind() {
    529        return doBooleanCommand("P2P_STOP_FIND");
    530     }
    531 
    532     public boolean p2pListen() {
    533         return doBooleanCommand("P2P_LISTEN");
    534     }
    535 
    536     public boolean p2pListen(int timeout) {
    537         if (timeout <= 0) {
    538             return p2pListen();
    539         }
    540         return doBooleanCommand("P2P_LISTEN " + timeout);
    541     }
    542 
    543     public boolean p2pFlush() {
    544         return doBooleanCommand("P2P_FLUSH");
    545     }
    546 
    547     /* p2p_connect <peer device address> <pbc|pin|PIN#> [label|display|keypad]
    548         [persistent] [join|auth] [go_intent=<0..15>] [freq=<in MHz>] */
    549     public String p2pConnect(WifiP2pConfig config, boolean joinExistingGroup) {
    550         if (config == null) return null;
    551         List<String> args = new ArrayList<String>();
    552         WpsInfo wps = config.wps;
    553         args.add(config.deviceAddress);
    554 
    555         switch (wps.setup) {
    556             case WpsInfo.PBC:
    557                 args.add("pbc");
    558                 break;
    559             case WpsInfo.DISPLAY:
    560                 if (TextUtils.isEmpty(wps.pin)) {
    561                     args.add("pin");
    562                 } else {
    563                     args.add(wps.pin);
    564                 }
    565                 args.add("display");
    566                 break;
    567             case WpsInfo.KEYPAD:
    568                 args.add(wps.pin);
    569                 args.add("keypad");
    570                 break;
    571             case WpsInfo.LABEL:
    572                 args.add(wps.pin);
    573                 args.add("label");
    574             default:
    575                 break;
    576         }
    577 
    578         if (config.netId == WifiP2pGroup.PERSISTENT_NET_ID) {
    579             args.add("persistent");
    580         }
    581 
    582         if (joinExistingGroup) {
    583             args.add("join");
    584         } else {
    585             //TODO: This can be adapted based on device plugged in state and
    586             //device battery state
    587             int groupOwnerIntent = config.groupOwnerIntent;
    588             if (groupOwnerIntent < 0 || groupOwnerIntent > 15) {
    589                 groupOwnerIntent = DEFAULT_GROUP_OWNER_INTENT;
    590             }
    591             args.add("go_intent=" + groupOwnerIntent);
    592         }
    593 
    594         String command = "P2P_CONNECT ";
    595         for (String s : args) command += s + " ";
    596 
    597         return doStringCommand(command);
    598     }
    599 
    600     public boolean p2pCancelConnect() {
    601         return doBooleanCommand("P2P_CANCEL");
    602     }
    603 
    604     public boolean p2pProvisionDiscovery(WifiP2pConfig config) {
    605         if (config == null) return false;
    606 
    607         switch (config.wps.setup) {
    608             case WpsInfo.PBC:
    609                 return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " pbc");
    610             case WpsInfo.DISPLAY:
    611                 //We are doing display, so provision discovery is keypad
    612                 return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " keypad");
    613             case WpsInfo.KEYPAD:
    614                 //We are doing keypad, so provision discovery is display
    615                 return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " display");
    616             default:
    617                 break;
    618         }
    619         return false;
    620     }
    621 
    622     public boolean p2pGroupAdd(boolean persistent) {
    623         if (persistent) {
    624             return doBooleanCommand("P2P_GROUP_ADD persistent");
    625         }
    626         return doBooleanCommand("P2P_GROUP_ADD");
    627     }
    628 
    629     public boolean p2pGroupAdd(int netId) {
    630         return doBooleanCommand("P2P_GROUP_ADD persistent=" + netId);
    631     }
    632 
    633     public boolean p2pGroupRemove(String iface) {
    634         if (TextUtils.isEmpty(iface)) return false;
    635         return doBooleanCommand("P2P_GROUP_REMOVE " + iface);
    636     }
    637 
    638     public boolean p2pReject(String deviceAddress) {
    639         return doBooleanCommand("P2P_REJECT " + deviceAddress);
    640     }
    641 
    642     /* Invite a peer to a group */
    643     public boolean p2pInvite(WifiP2pGroup group, String deviceAddress) {
    644         if (TextUtils.isEmpty(deviceAddress)) return false;
    645 
    646         if (group == null) {
    647             return doBooleanCommand("P2P_INVITE peer=" + deviceAddress);
    648         } else {
    649             return doBooleanCommand("P2P_INVITE group=" + group.getInterface()
    650                     + " peer=" + deviceAddress + " go_dev_addr=" + group.getOwner().deviceAddress);
    651         }
    652     }
    653 
    654     /* Reinvoke a persistent connection */
    655     public boolean p2pReinvoke(int netId, String deviceAddress) {
    656         if (TextUtils.isEmpty(deviceAddress) || netId < 0) return false;
    657 
    658         return doBooleanCommand("P2P_INVITE persistent=" + netId + " peer=" + deviceAddress);
    659     }
    660 
    661     public String p2pGetSsid(String deviceAddress) {
    662         return p2pGetParam(deviceAddress, "oper_ssid");
    663     }
    664 
    665     public String p2pGetDeviceAddress() {
    666         String status = status();
    667         if (status == null) return "";
    668 
    669         String[] tokens = status.split("\n");
    670         for (String token : tokens) {
    671             if (token.startsWith("p2p_device_address=")) {
    672                 String[] nameValue = token.split("=");
    673                 if (nameValue.length != 2) break;
    674                 return nameValue[1];
    675             }
    676         }
    677         return "";
    678     }
    679 
    680     public int getGroupCapability(String deviceAddress) {
    681         int gc = 0;
    682         if (TextUtils.isEmpty(deviceAddress)) return gc;
    683         String peerInfo = p2pPeer(deviceAddress);
    684         if (TextUtils.isEmpty(peerInfo)) return gc;
    685 
    686         String[] tokens = peerInfo.split("\n");
    687         for (String token : tokens) {
    688             if (token.startsWith("group_capab=")) {
    689                 String[] nameValue = token.split("=");
    690                 if (nameValue.length != 2) break;
    691                 try {
    692                     return Integer.decode(nameValue[1]);
    693                 } catch(NumberFormatException e) {
    694                     return gc;
    695                 }
    696             }
    697         }
    698         return gc;
    699     }
    700 
    701     public String p2pPeer(String deviceAddress) {
    702         return doStringCommand("P2P_PEER " + deviceAddress);
    703     }
    704 
    705     private String p2pGetParam(String deviceAddress, String key) {
    706         if (deviceAddress == null) return null;
    707 
    708         String peerInfo = p2pPeer(deviceAddress);
    709         if (peerInfo == null) return null;
    710         String[] tokens= peerInfo.split("\n");
    711 
    712         key += "=";
    713         for (String token : tokens) {
    714             if (token.startsWith(key)) {
    715                 String[] nameValue = token.split("=");
    716                 if (nameValue.length != 2) break;
    717                 return nameValue[1];
    718             }
    719         }
    720         return null;
    721     }
    722 
    723     public boolean p2pServiceAdd(WifiP2pServiceInfo servInfo) {
    724         /*
    725          * P2P_SERVICE_ADD bonjour <query hexdump> <RDATA hexdump>
    726          * P2P_SERVICE_ADD upnp <version hex> <service>
    727          *
    728          * e.g)
    729          * [Bonjour]
    730          * # IP Printing over TCP (PTR) (RDATA=MyPrinter._ipp._tcp.local.)
    731          * P2P_SERVICE_ADD bonjour 045f697070c00c000c01 094d795072696e746572c027
    732          * # IP Printing over TCP (TXT) (RDATA=txtvers=1,pdl=application/postscript)
    733          * P2P_SERVICE_ADD bonjour 096d797072696e746572045f697070c00c001001
    734          *  09747874766572733d311a70646c3d6170706c69636174696f6e2f706f7374736372797074
    735          *
    736          * [UPnP]
    737          * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012
    738          * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice
    739          * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::urn:schemas-upnp
    740          * -org:device:InternetGatewayDevice:1
    741          * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9322-123456789012::urn:schemas-upnp
    742          * -org:service:ContentDirectory:2
    743          */
    744         for (String s : servInfo.getSupplicantQueryList()) {
    745             String command = "P2P_SERVICE_ADD";
    746             command += (" " + s);
    747             if (!doBooleanCommand(command)) {
    748                 return false;
    749             }
    750         }
    751         return true;
    752     }
    753 
    754     public boolean p2pServiceDel(WifiP2pServiceInfo servInfo) {
    755         /*
    756          * P2P_SERVICE_DEL bonjour <query hexdump>
    757          * P2P_SERVICE_DEL upnp <version hex> <service>
    758          */
    759         for (String s : servInfo.getSupplicantQueryList()) {
    760             String command = "P2P_SERVICE_DEL ";
    761 
    762             String[] data = s.split(" ");
    763             if (data.length < 2) {
    764                 return false;
    765             }
    766             if ("upnp".equals(data[0])) {
    767                 command += s;
    768             } else if ("bonjour".equals(data[0])) {
    769                 command += data[0];
    770                 command += (" " + data[1]);
    771             } else {
    772                 return false;
    773             }
    774             if (!doBooleanCommand(command)) {
    775                 return false;
    776             }
    777         }
    778         return true;
    779     }
    780 
    781     public boolean p2pServiceFlush() {
    782         return doBooleanCommand("P2P_SERVICE_FLUSH");
    783     }
    784 
    785     public String p2pServDiscReq(String addr, String query) {
    786         String command = "P2P_SERV_DISC_REQ";
    787         command += (" " + addr);
    788         command += (" " + query);
    789 
    790         return doStringCommand(command);
    791     }
    792 
    793     public boolean p2pServDiscCancelReq(String id) {
    794         return doBooleanCommand("P2P_SERV_DISC_CANCEL_REQ " + id);
    795     }
    796 
    797     /* Set the current mode of miracast operation.
    798      *  0 = disabled
    799      *  1 = operating as source
    800      *  2 = operating as sink
    801      */
    802     public void setMiracastMode(int mode) {
    803         // Note: optional feature on the driver. It is ok for this to fail.
    804         doBooleanCommand("DRIVER MIRACAST " + mode);
    805     }
    806 }
    807