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 com.android.server.wifi;
     18 
     19 import android.net.wifi.BatchedScanSettings;
     20 import android.net.wifi.RttManager;
     21 import android.net.wifi.ScanResult;
     22 import android.net.wifi.WifiLinkLayerStats;
     23 import android.net.wifi.WifiScanner;
     24 import android.net.wifi.WpsInfo;
     25 import android.net.wifi.p2p.WifiP2pConfig;
     26 import android.net.wifi.p2p.WifiP2pGroup;
     27 import android.os.SystemClock;
     28 import android.text.TextUtils;
     29 import android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
     30 import android.util.LocalLog;
     31 import android.util.Log;
     32 
     33 import java.util.ArrayList;
     34 import java.util.List;
     35 import java.util.Locale;
     36 
     37 /**
     38  * Native calls for bring up/shut down of the supplicant daemon and for
     39  * sending requests to the supplicant daemon
     40  *
     41  * waitForEvent() is called on the monitor thread for events. All other methods
     42  * must be serialized from the framework.
     43  *
     44  * {@hide}
     45  */
     46 public class WifiNative {
     47 
     48     private static boolean DBG = false;
     49     private final String mTAG;
     50     private static final int DEFAULT_GROUP_OWNER_INTENT     = 6;
     51 
     52     static final int BLUETOOTH_COEXISTENCE_MODE_ENABLED     = 0;
     53     static final int BLUETOOTH_COEXISTENCE_MODE_DISABLED    = 1;
     54     static final int BLUETOOTH_COEXISTENCE_MODE_SENSE       = 2;
     55 
     56     static final int SCAN_WITHOUT_CONNECTION_SETUP          = 1;
     57     static final int SCAN_WITH_CONNECTION_SETUP             = 2;
     58 
     59     // Hold this lock before calling supplicant - it is required to
     60     // mutually exclude access from Wifi and P2p state machines
     61     static final Object mLock = new Object();
     62 
     63     public final String mInterfaceName;
     64     public final String mInterfacePrefix;
     65 
     66     private boolean mSuspendOptEnabled = false;
     67 
     68     /* Register native functions */
     69 
     70     static {
     71         /* Native functions are defined in libwifi-service.so */
     72         System.loadLibrary("wifi-service");
     73         registerNatives();
     74     }
     75 
     76     private static native int registerNatives();
     77 
     78     public native static boolean loadDriver();
     79 
     80     public native static boolean isDriverLoaded();
     81 
     82     public native static boolean unloadDriver();
     83 
     84     public native static boolean startSupplicant(boolean p2pSupported);
     85 
     86     /* Sends a kill signal to supplicant. To be used when we have lost connection
     87        or when the supplicant is hung */
     88     public native static boolean killSupplicant(boolean p2pSupported);
     89 
     90     private native boolean connectToSupplicantNative();
     91 
     92     private native void closeSupplicantConnectionNative();
     93 
     94     /**
     95      * Wait for the supplicant to send an event, returning the event string.
     96      * @return the event string sent by the supplicant.
     97      */
     98     private native String waitForEventNative();
     99 
    100     private native boolean doBooleanCommandNative(String command);
    101 
    102     private native int doIntCommandNative(String command);
    103 
    104     private native String doStringCommandNative(String command);
    105 
    106     public WifiNative(String interfaceName) {
    107         mInterfaceName = interfaceName;
    108         mTAG = "WifiNative-" + interfaceName;
    109         if (!interfaceName.equals("p2p0")) {
    110             mInterfacePrefix = "IFNAME=" + interfaceName + " ";
    111         } else {
    112             // commands for p2p0 interface don't need prefix
    113             mInterfacePrefix = "";
    114         }
    115     }
    116 
    117     void enableVerboseLogging(int verbose) {
    118         if (verbose > 0) {
    119             DBG = true;
    120         } else {
    121             DBG = false;
    122         }
    123     }
    124 
    125     private static final LocalLog mLocalLog = new LocalLog(1024);
    126 
    127     // hold mLock before accessing mCmdIdLock
    128     private static int sCmdId;
    129 
    130     public LocalLog getLocalLog() {
    131         return mLocalLog;
    132     }
    133 
    134     private static int getNewCmdIdLocked() {
    135         return sCmdId++;
    136     }
    137 
    138     private void localLog(String s) {
    139         if (mLocalLog != null)
    140             mLocalLog.log(mInterfaceName + ": " + s);
    141     }
    142 
    143     public boolean connectToSupplicant() {
    144         // No synchronization necessary .. it is implemented in WifiMonitor
    145         localLog(mInterfacePrefix + "connectToSupplicant");
    146         return connectToSupplicantNative();
    147     }
    148 
    149     public void closeSupplicantConnection() {
    150         localLog(mInterfacePrefix + "closeSupplicantConnection");
    151         closeSupplicantConnectionNative();
    152     }
    153 
    154     public String waitForEvent() {
    155         // No synchronization necessary .. it is implemented in WifiMonitor
    156         return waitForEventNative();
    157     }
    158 
    159     private boolean doBooleanCommand(String command) {
    160         if (DBG) Log.d(mTAG, "doBoolean: " + command);
    161         synchronized (mLock) {
    162             int cmdId = getNewCmdIdLocked();
    163             String toLog = Integer.toString(cmdId) + ":" + mInterfacePrefix + command;
    164             boolean result = doBooleanCommandNative(mInterfacePrefix + command);
    165             localLog(toLog + " -> " + result);
    166             if (DBG) Log.d(mTAG, command + ": returned " + result);
    167             return result;
    168         }
    169     }
    170 
    171     private int doIntCommand(String command) {
    172         if (DBG) Log.d(mTAG, "doInt: " + command);
    173         synchronized (mLock) {
    174             int cmdId = getNewCmdIdLocked();
    175             String toLog = Integer.toString(cmdId) + ":" + mInterfacePrefix + command;
    176             int result = doIntCommandNative(mInterfacePrefix + command);
    177             localLog(toLog + " -> " + result);
    178             if (DBG) Log.d(mTAG, "   returned " + result);
    179             return result;
    180         }
    181     }
    182 
    183     private String doStringCommand(String command) {
    184         if (DBG) {
    185             //GET_NETWORK commands flood the logs
    186             if (!command.startsWith("GET_NETWORK")) {
    187                 Log.d(mTAG, "doString: [" + command + "]");
    188             }
    189         }
    190         synchronized (mLock) {
    191             int cmdId = getNewCmdIdLocked();
    192             String toLog = Integer.toString(cmdId) + ":" + mInterfacePrefix + command;
    193             String result = doStringCommandNative(mInterfacePrefix + command);
    194             if (result == null) {
    195                 if (DBG) Log.d(mTAG, "doStringCommandNative no result");
    196             } else {
    197                 if (!command.startsWith("STATUS-")) {
    198                     localLog(toLog + " -> " + result);
    199                 }
    200                 if (DBG) Log.d(mTAG, "   returned " + result.replace("\n", " "));
    201             }
    202             return result;
    203         }
    204     }
    205 
    206     private String doStringCommandWithoutLogging(String command) {
    207         if (DBG) {
    208             //GET_NETWORK commands flood the logs
    209             if (!command.startsWith("GET_NETWORK")) {
    210                 Log.d(mTAG, "doString: [" + command + "]");
    211             }
    212         }
    213         synchronized (mLock) {
    214             return doStringCommandNative(mInterfacePrefix + command);
    215         }
    216     }
    217 
    218     public boolean ping() {
    219         String pong = doStringCommand("PING");
    220         return (pong != null && pong.equals("PONG"));
    221     }
    222 
    223     public void setSupplicantLogLevel(String level) {
    224         doStringCommand("LOG_LEVEL " + level);
    225     }
    226 
    227     public String getFreqCapability() {
    228         return doStringCommand("GET_CAPABILITY freq");
    229     }
    230 
    231     public boolean scan(int type, String freqList) {
    232         if (type == SCAN_WITHOUT_CONNECTION_SETUP) {
    233             if (freqList == null) return doBooleanCommand("SCAN TYPE=ONLY");
    234             else return doBooleanCommand("SCAN TYPE=ONLY freq=" + freqList);
    235         } else if (type == SCAN_WITH_CONNECTION_SETUP) {
    236             if (freqList == null) return doBooleanCommand("SCAN");
    237             else return doBooleanCommand("SCAN freq=" + freqList);
    238         } else {
    239             throw new IllegalArgumentException("Invalid scan type");
    240         }
    241     }
    242 
    243     /* Does a graceful shutdown of supplicant. Is a common stop function for both p2p and sta.
    244      *
    245      * Note that underneath we use a harsh-sounding "terminate" supplicant command
    246      * for a graceful stop and a mild-sounding "stop" interface
    247      * to kill the process
    248      */
    249     public boolean stopSupplicant() {
    250         return doBooleanCommand("TERMINATE");
    251     }
    252 
    253     public String listNetworks() {
    254         return doStringCommand("LIST_NETWORKS");
    255     }
    256 
    257     public int addNetwork() {
    258         return doIntCommand("ADD_NETWORK");
    259     }
    260 
    261     public boolean setNetworkVariable(int netId, String name, String value) {
    262         if (TextUtils.isEmpty(name) || TextUtils.isEmpty(value)) return false;
    263         return doBooleanCommand("SET_NETWORK " + netId + " " + name + " " + value);
    264     }
    265 
    266     public String getNetworkVariable(int netId, String name) {
    267         if (TextUtils.isEmpty(name)) return null;
    268 
    269         // GET_NETWORK will likely flood the logs ...
    270         return doStringCommandWithoutLogging("GET_NETWORK " + netId + " " + name);
    271     }
    272 
    273     public boolean removeNetwork(int netId) {
    274         return doBooleanCommand("REMOVE_NETWORK " + netId);
    275     }
    276 
    277 
    278     private void logDbg(String debug) {
    279         long now = SystemClock.elapsedRealtimeNanos();
    280         String ts = String.format("[%,d us] ", now/1000);
    281         Log.e("WifiNative: ", ts+debug+ " stack:"
    282                 + Thread.currentThread().getStackTrace()[2].getMethodName() +" - "
    283                 + Thread.currentThread().getStackTrace()[3].getMethodName() +" - "
    284                 + Thread.currentThread().getStackTrace()[4].getMethodName() +" - "
    285                 + Thread.currentThread().getStackTrace()[5].getMethodName()+" - "
    286                 + Thread.currentThread().getStackTrace()[6].getMethodName());
    287 
    288     }
    289     public boolean enableNetwork(int netId, boolean disableOthers) {
    290         if (DBG) logDbg("enableNetwork nid=" + Integer.toString(netId)
    291                 + " disableOthers=" + disableOthers);
    292         if (disableOthers) {
    293             return doBooleanCommand("SELECT_NETWORK " + netId);
    294         } else {
    295             return doBooleanCommand("ENABLE_NETWORK " + netId);
    296         }
    297     }
    298 
    299     public boolean disableNetwork(int netId) {
    300         if (DBG) logDbg("disableNetwork nid=" + Integer.toString(netId));
    301         return doBooleanCommand("DISABLE_NETWORK " + netId);
    302     }
    303 
    304     public boolean reconnect() {
    305         if (DBG) logDbg("RECONNECT ");
    306         return doBooleanCommand("RECONNECT");
    307     }
    308 
    309     public boolean reassociate() {
    310         if (DBG) logDbg("REASSOCIATE ");
    311         return doBooleanCommand("REASSOCIATE");
    312     }
    313 
    314     public boolean disconnect() {
    315         if (DBG) logDbg("DISCONNECT ");
    316         return doBooleanCommand("DISCONNECT");
    317     }
    318 
    319     public String status() {
    320         return status(false);
    321     }
    322 
    323     public String status(boolean noEvents) {
    324         if (noEvents) {
    325             return doStringCommand("STATUS-NO_EVENTS");
    326         } else {
    327             return doStringCommand("STATUS");
    328         }
    329     }
    330 
    331     public String getMacAddress() {
    332         //Macaddr = XX.XX.XX.XX.XX.XX
    333         String ret = doStringCommand("DRIVER MACADDR");
    334         if (!TextUtils.isEmpty(ret)) {
    335             String[] tokens = ret.split(" = ");
    336             if (tokens.length == 2) return tokens[1];
    337         }
    338         return null;
    339     }
    340 
    341     /**
    342      * Format of results:
    343      * =================
    344      * id=1
    345      * bssid=68:7f:74:d7:1b:6e
    346      * freq=2412
    347      * level=-43
    348      * tsf=1344621975160944
    349      * age=2623
    350      * flags=[WPA2-PSK-CCMP][WPS][ESS]
    351      * ssid=zubyb
    352      * ====
    353      *
    354      * RANGE=ALL gets all scan results
    355      * RANGE=ID- gets results from ID
    356      * MASK=<N> see wpa_supplicant/src/common/wpa_ctrl.h for details
    357      */
    358     public String scanResults(int sid) {
    359         return doStringCommandWithoutLogging("BSS RANGE=" + sid + "- MASK=0x21987");
    360     }
    361 
    362     /**
    363      * Format of result:
    364      * id=1016
    365      * bssid=00:03:7f:40:84:10
    366      * freq=2462
    367      * beacon_int=200
    368      * capabilities=0x0431
    369      * qual=0
    370      * noise=0
    371      * level=-46
    372      * tsf=0000002669008476
    373      * age=5
    374      * ie=00105143412d485332302d52322d54455354010882848b960c12182403010b0706555...
    375      * flags=[WPA2-EAP-CCMP][ESS][P2P][HS20]
    376      * ssid=QCA-HS20-R2-TEST
    377      * p2p_device_name=
    378      * p2p_config_methods=0x0SET_NE
    379      * anqp_venue_name=02083d656e6757692d466920416c6c69616e63650a3239383920436f...
    380      * anqp_network_auth_type=010000
    381      * anqp_roaming_consortium=03506f9a05001bc504bd
    382      * anqp_ip_addr_type_availability=0c
    383      * anqp_nai_realm=0200300000246d61696c2e6578616d706c652e636f6d3b636973636f2...
    384      * anqp_3gpp=000600040132f465
    385      * anqp_domain_name=0b65786d61706c652e636f6d
    386      * hs20_operator_friendly_name=11656e6757692d466920416c6c69616e63650e636869...
    387      * hs20_wan_metrics=01c40900008001000000000a00
    388      * hs20_connection_capability=0100000006140001061600000650000106bb010106bb0...
    389      * hs20_osu_providers_list=0b5143412d4f53552d425353010901310015656e6757692d...
    390      */
    391     public String scanResult(String bssid) {
    392         return doStringCommand("BSS " + bssid);
    393     }
    394 
    395     /**
    396      * Format of command
    397      * DRIVER WLS_BATCHING SET SCANFREQ=x MSCAN=r BESTN=y CHANNEL=<z, w, t> RTT=s
    398      * where x is an ascii representation of an integer number of seconds between scans
    399      *       r is an ascii representation of an integer number of scans per batch
    400      *       y is an ascii representation of an integer number of the max AP to remember per scan
    401      *       z, w, t represent a 1..n size list of channel numbers and/or 'A', 'B' values
    402      *           indicating entire ranges of channels
    403      *       s is an ascii representation of an integer number of highest-strength AP
    404      *           for which we'd like approximate distance reported
    405      *
    406      * The return value is an ascii integer representing a guess of the number of scans
    407      * the firmware can remember before it runs out of buffer space or -1 on error
    408      */
    409     public String setBatchedScanSettings(BatchedScanSettings settings) {
    410         if (settings == null) {
    411             return doStringCommand("DRIVER WLS_BATCHING STOP");
    412         }
    413         String cmd = "DRIVER WLS_BATCHING SET SCANFREQ=" + settings.scanIntervalSec;
    414         cmd += " MSCAN=" + settings.maxScansPerBatch;
    415         if (settings.maxApPerScan != BatchedScanSettings.UNSPECIFIED) {
    416             cmd += " BESTN=" + settings.maxApPerScan;
    417         }
    418         if (settings.channelSet != null && !settings.channelSet.isEmpty()) {
    419             cmd += " CHANNEL=<";
    420             int i = 0;
    421             for (String channel : settings.channelSet) {
    422                 cmd += (i > 0 ? "," : "") + channel;
    423                 ++i;
    424             }
    425             cmd += ">";
    426         }
    427         if (settings.maxApForDistance != BatchedScanSettings.UNSPECIFIED) {
    428             cmd += " RTT=" + settings.maxApForDistance;
    429         }
    430         return doStringCommand(cmd);
    431     }
    432 
    433     public String getBatchedScanResults() {
    434         return doStringCommand("DRIVER WLS_BATCHING GET");
    435     }
    436 
    437     public boolean startDriver() {
    438         return doBooleanCommand("DRIVER START");
    439     }
    440 
    441     public boolean stopDriver() {
    442         return doBooleanCommand("DRIVER STOP");
    443     }
    444 
    445 
    446     /**
    447      * Start filtering out Multicast V4 packets
    448      * @return {@code true} if the operation succeeded, {@code false} otherwise
    449      *
    450      * Multicast filtering rules work as follows:
    451      *
    452      * The driver can filter multicast (v4 and/or v6) and broadcast packets when in
    453      * a power optimized mode (typically when screen goes off).
    454      *
    455      * In order to prevent the driver from filtering the multicast/broadcast packets, we have to
    456      * add a DRIVER RXFILTER-ADD rule followed by DRIVER RXFILTER-START to make the rule effective
    457      *
    458      * DRIVER RXFILTER-ADD Num
    459      *   where Num = 0 - Unicast, 1 - Broadcast, 2 - Mutil4 or 3 - Multi6
    460      *
    461      * and DRIVER RXFILTER-START
    462      * In order to stop the usage of these rules, we do
    463      *
    464      * DRIVER RXFILTER-STOP
    465      * DRIVER RXFILTER-REMOVE Num
    466      *   where Num is as described for RXFILTER-ADD
    467      *
    468      * The  SETSUSPENDOPT driver command overrides the filtering rules
    469      */
    470     public boolean startFilteringMulticastV4Packets() {
    471         return doBooleanCommand("DRIVER RXFILTER-STOP")
    472             && doBooleanCommand("DRIVER RXFILTER-REMOVE 2")
    473             && doBooleanCommand("DRIVER RXFILTER-START");
    474     }
    475 
    476     /**
    477      * Stop filtering out Multicast V4 packets.
    478      * @return {@code true} if the operation succeeded, {@code false} otherwise
    479      */
    480     public boolean stopFilteringMulticastV4Packets() {
    481         return doBooleanCommand("DRIVER RXFILTER-STOP")
    482             && doBooleanCommand("DRIVER RXFILTER-ADD 2")
    483             && doBooleanCommand("DRIVER RXFILTER-START");
    484     }
    485 
    486     /**
    487      * Start filtering out Multicast V6 packets
    488      * @return {@code true} if the operation succeeded, {@code false} otherwise
    489      */
    490     public boolean startFilteringMulticastV6Packets() {
    491         return doBooleanCommand("DRIVER RXFILTER-STOP")
    492             && doBooleanCommand("DRIVER RXFILTER-REMOVE 3")
    493             && doBooleanCommand("DRIVER RXFILTER-START");
    494     }
    495 
    496     /**
    497      * Stop filtering out Multicast V6 packets.
    498      * @return {@code true} if the operation succeeded, {@code false} otherwise
    499      */
    500     public boolean stopFilteringMulticastV6Packets() {
    501         return doBooleanCommand("DRIVER RXFILTER-STOP")
    502             && doBooleanCommand("DRIVER RXFILTER-ADD 3")
    503             && doBooleanCommand("DRIVER RXFILTER-START");
    504     }
    505 
    506     public int getBand() {
    507        String ret = doStringCommand("DRIVER GETBAND");
    508         if (!TextUtils.isEmpty(ret)) {
    509             //reply is "BAND X" where X is the band
    510             String[] tokens = ret.split(" ");
    511             try {
    512                 if (tokens.length == 2) return Integer.parseInt(tokens[1]);
    513             } catch (NumberFormatException e) {
    514                 return -1;
    515             }
    516         }
    517         return -1;
    518     }
    519 
    520     public boolean setBand(int band) {
    521         return doBooleanCommand("DRIVER SETBAND " + band);
    522     }
    523 
    524     /**
    525       * Sets the bluetooth coexistence mode.
    526       *
    527       * @param mode One of {@link #BLUETOOTH_COEXISTENCE_MODE_DISABLED},
    528       *            {@link #BLUETOOTH_COEXISTENCE_MODE_ENABLED}, or
    529       *            {@link #BLUETOOTH_COEXISTENCE_MODE_SENSE}.
    530       * @return Whether the mode was successfully set.
    531       */
    532     public boolean setBluetoothCoexistenceMode(int mode) {
    533         return doBooleanCommand("DRIVER BTCOEXMODE " + mode);
    534     }
    535 
    536     /**
    537      * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
    538      * some of the low-level scan parameters used by the driver are changed to
    539      * reduce interference with A2DP streaming.
    540      *
    541      * @param isSet whether to enable or disable this mode
    542      * @return {@code true} if the command succeeded, {@code false} otherwise.
    543      */
    544     public boolean setBluetoothCoexistenceScanMode(boolean setCoexScanMode) {
    545         if (setCoexScanMode) {
    546             return doBooleanCommand("DRIVER BTCOEXSCAN-START");
    547         } else {
    548             return doBooleanCommand("DRIVER BTCOEXSCAN-STOP");
    549         }
    550     }
    551 
    552     public void enableSaveConfig() {
    553         doBooleanCommand("SET update_config 1");
    554     }
    555 
    556     public boolean saveConfig() {
    557         return doBooleanCommand("SAVE_CONFIG");
    558     }
    559 
    560     public boolean addToBlacklist(String bssid) {
    561         if (TextUtils.isEmpty(bssid)) return false;
    562         return doBooleanCommand("BLACKLIST " + bssid);
    563     }
    564 
    565     public boolean clearBlacklist() {
    566         return doBooleanCommand("BLACKLIST clear");
    567     }
    568 
    569     public boolean setSuspendOptimizations(boolean enabled) {
    570        // if (mSuspendOptEnabled == enabled) return true;
    571         mSuspendOptEnabled = enabled;
    572 
    573         Log.e("native", "do suspend " + enabled);
    574         if (enabled) {
    575             return doBooleanCommand("DRIVER SETSUSPENDMODE 1");
    576         } else {
    577             return doBooleanCommand("DRIVER SETSUSPENDMODE 0");
    578         }
    579     }
    580 
    581     public boolean setCountryCode(String countryCode) {
    582         return doBooleanCommand("DRIVER COUNTRY " + countryCode.toUpperCase(Locale.ROOT));
    583     }
    584 
    585     public void enableBackgroundScan(boolean enable) {
    586         if (enable) {
    587             doBooleanCommand("SET pno 1");
    588         } else {
    589             doBooleanCommand("SET pno 0");
    590         }
    591     }
    592 
    593     public void enableAutoConnect(boolean enable) {
    594         if (enable) {
    595             doBooleanCommand("STA_AUTOCONNECT 1");
    596         } else {
    597             doBooleanCommand("STA_AUTOCONNECT 0");
    598         }
    599     }
    600 
    601     public void setScanInterval(int scanInterval) {
    602         doBooleanCommand("SCAN_INTERVAL " + scanInterval);
    603     }
    604 
    605     public void startTdls(String macAddr, boolean enable) {
    606         if (enable) {
    607             doBooleanCommand("TDLS_DISCOVER " + macAddr);
    608             doBooleanCommand("TDLS_SETUP " + macAddr);
    609         } else {
    610             doBooleanCommand("TDLS_TEARDOWN " + macAddr);
    611         }
    612     }
    613 
    614     /** Example output:
    615      * RSSI=-65
    616      * LINKSPEED=48
    617      * NOISE=9999
    618      * FREQUENCY=0
    619      */
    620     public String signalPoll() {
    621         return doStringCommandWithoutLogging("SIGNAL_POLL");
    622     }
    623 
    624     /** Example outout:
    625      * TXGOOD=396
    626      * TXBAD=1
    627      */
    628     public String pktcntPoll() {
    629         return doStringCommand("PKTCNT_POLL");
    630     }
    631 
    632     public void bssFlush() {
    633         doBooleanCommand("BSS_FLUSH 0");
    634     }
    635 
    636     public boolean startWpsPbc(String bssid) {
    637         if (TextUtils.isEmpty(bssid)) {
    638             return doBooleanCommand("WPS_PBC");
    639         } else {
    640             return doBooleanCommand("WPS_PBC " + bssid);
    641         }
    642     }
    643 
    644     public boolean startWpsPbc(String iface, String bssid) {
    645         synchronized (mLock) {
    646             if (TextUtils.isEmpty(bssid)) {
    647                 return doBooleanCommandNative("IFNAME=" + iface + " WPS_PBC");
    648             } else {
    649                 return doBooleanCommandNative("IFNAME=" + iface + " WPS_PBC " + bssid);
    650             }
    651         }
    652     }
    653 
    654     public boolean startWpsPinKeypad(String pin) {
    655         if (TextUtils.isEmpty(pin)) return false;
    656         return doBooleanCommand("WPS_PIN any " + pin);
    657     }
    658 
    659     public boolean startWpsPinKeypad(String iface, String pin) {
    660         if (TextUtils.isEmpty(pin)) return false;
    661         synchronized (mLock) {
    662             return doBooleanCommandNative("IFNAME=" + iface + " WPS_PIN any " + pin);
    663         }
    664     }
    665 
    666 
    667     public String startWpsPinDisplay(String bssid) {
    668         if (TextUtils.isEmpty(bssid)) {
    669             return doStringCommand("WPS_PIN any");
    670         } else {
    671             return doStringCommand("WPS_PIN " + bssid);
    672         }
    673     }
    674 
    675     public String startWpsPinDisplay(String iface, String bssid) {
    676         synchronized (mLock) {
    677             if (TextUtils.isEmpty(bssid)) {
    678                 return doStringCommandNative("IFNAME=" + iface + " WPS_PIN any");
    679             } else {
    680                 return doStringCommandNative("IFNAME=" + iface + " WPS_PIN " + bssid);
    681             }
    682         }
    683     }
    684 
    685     public boolean setExternalSim(boolean external) {
    686         synchronized (mLock) {
    687             String value = external ? "1" : "0";
    688             Log.d(TAG, "Setting external_sim to " + value);
    689             return doBooleanCommand("SET external_sim " + value);
    690         }
    691     }
    692 
    693     public boolean simAuthResponse(int id, String response) {
    694         synchronized (mLock) {
    695             return doBooleanCommand("CTRL-RSP-SIM-" + id + ":GSM-AUTH" + response);
    696         }
    697     }
    698 
    699     /* Configures an access point connection */
    700     public boolean startWpsRegistrar(String bssid, String pin) {
    701         if (TextUtils.isEmpty(bssid) || TextUtils.isEmpty(pin)) return false;
    702         return doBooleanCommand("WPS_REG " + bssid + " " + pin);
    703     }
    704 
    705     public boolean cancelWps() {
    706         return doBooleanCommand("WPS_CANCEL");
    707     }
    708 
    709     public boolean setPersistentReconnect(boolean enabled) {
    710         int value = (enabled == true) ? 1 : 0;
    711         return doBooleanCommand("SET persistent_reconnect " + value);
    712     }
    713 
    714     public boolean setDeviceName(String name) {
    715         return doBooleanCommand("SET device_name " + name);
    716     }
    717 
    718     public boolean setDeviceType(String type) {
    719         return doBooleanCommand("SET device_type " + type);
    720     }
    721 
    722     public boolean setConfigMethods(String cfg) {
    723         return doBooleanCommand("SET config_methods " + cfg);
    724     }
    725 
    726     public boolean setManufacturer(String value) {
    727         return doBooleanCommand("SET manufacturer " + value);
    728     }
    729 
    730     public boolean setModelName(String value) {
    731         return doBooleanCommand("SET model_name " + value);
    732     }
    733 
    734     public boolean setModelNumber(String value) {
    735         return doBooleanCommand("SET model_number " + value);
    736     }
    737 
    738     public boolean setSerialNumber(String value) {
    739         return doBooleanCommand("SET serial_number " + value);
    740     }
    741 
    742     public boolean setP2pSsidPostfix(String postfix) {
    743         return doBooleanCommand("SET p2p_ssid_postfix " + postfix);
    744     }
    745 
    746     public boolean setP2pGroupIdle(String iface, int time) {
    747         synchronized (mLock) {
    748             return doBooleanCommandNative("IFNAME=" + iface + " SET p2p_group_idle " + time);
    749         }
    750     }
    751 
    752     public void setPowerSave(boolean enabled) {
    753         if (enabled) {
    754             doBooleanCommand("SET ps 1");
    755         } else {
    756             doBooleanCommand("SET ps 0");
    757         }
    758     }
    759 
    760     public boolean setP2pPowerSave(String iface, boolean enabled) {
    761         synchronized (mLock) {
    762             if (enabled) {
    763                 return doBooleanCommandNative("IFNAME=" + iface + " P2P_SET ps 1");
    764             } else {
    765                 return doBooleanCommandNative("IFNAME=" + iface + " P2P_SET ps 0");
    766             }
    767         }
    768     }
    769 
    770     public boolean setWfdEnable(boolean enable) {
    771         return doBooleanCommand("SET wifi_display " + (enable ? "1" : "0"));
    772     }
    773 
    774     public boolean setWfdDeviceInfo(String hex) {
    775         return doBooleanCommand("WFD_SUBELEM_SET 0 " + hex);
    776     }
    777 
    778     /**
    779      * "sta" prioritizes STA connection over P2P and "p2p" prioritizes
    780      * P2P connection over STA
    781      */
    782     public boolean setConcurrencyPriority(String s) {
    783         return doBooleanCommand("P2P_SET conc_pref " + s);
    784     }
    785 
    786     public boolean p2pFind() {
    787         return doBooleanCommand("P2P_FIND");
    788     }
    789 
    790     public boolean p2pFind(int timeout) {
    791         if (timeout <= 0) {
    792             return p2pFind();
    793         }
    794         return doBooleanCommand("P2P_FIND " + timeout);
    795     }
    796 
    797     public boolean p2pStopFind() {
    798        return doBooleanCommand("P2P_STOP_FIND");
    799     }
    800 
    801     public boolean p2pListen() {
    802         return doBooleanCommand("P2P_LISTEN");
    803     }
    804 
    805     public boolean p2pListen(int timeout) {
    806         if (timeout <= 0) {
    807             return p2pListen();
    808         }
    809         return doBooleanCommand("P2P_LISTEN " + timeout);
    810     }
    811 
    812     public boolean p2pExtListen(boolean enable, int period, int interval) {
    813         if (enable && interval < period) {
    814             return false;
    815         }
    816         return doBooleanCommand("P2P_EXT_LISTEN"
    817                     + (enable ? (" " + period + " " + interval) : ""));
    818     }
    819 
    820     public boolean p2pSetChannel(int lc, int oc) {
    821         if (DBG) Log.d(mTAG, "p2pSetChannel: lc="+lc+", oc="+oc);
    822 
    823         if (lc >=1 && lc <= 11) {
    824             if (!doBooleanCommand("P2P_SET listen_channel " + lc)) {
    825                 return false;
    826             }
    827         } else if (lc != 0) {
    828             return false;
    829         }
    830 
    831         if (oc >= 1 && oc <= 165 ) {
    832             int freq = (oc <= 14 ? 2407 : 5000) + oc * 5;
    833             return doBooleanCommand("P2P_SET disallow_freq 1000-"
    834                     + (freq - 5) + "," + (freq + 5) + "-6000");
    835         } else if (oc == 0) {
    836             /* oc==0 disables "P2P_SET disallow_freq" (enables all freqs) */
    837             return doBooleanCommand("P2P_SET disallow_freq \"\"");
    838         }
    839 
    840         return false;
    841     }
    842 
    843     public boolean p2pFlush() {
    844         return doBooleanCommand("P2P_FLUSH");
    845     }
    846 
    847     /* p2p_connect <peer device address> <pbc|pin|PIN#> [label|display|keypad]
    848         [persistent] [join|auth] [go_intent=<0..15>] [freq=<in MHz>] */
    849     public String p2pConnect(WifiP2pConfig config, boolean joinExistingGroup) {
    850         if (config == null) return null;
    851         List<String> args = new ArrayList<String>();
    852         WpsInfo wps = config.wps;
    853         args.add(config.deviceAddress);
    854 
    855         switch (wps.setup) {
    856             case WpsInfo.PBC:
    857                 args.add("pbc");
    858                 break;
    859             case WpsInfo.DISPLAY:
    860                 if (TextUtils.isEmpty(wps.pin)) {
    861                     args.add("pin");
    862                 } else {
    863                     args.add(wps.pin);
    864                 }
    865                 args.add("display");
    866                 break;
    867             case WpsInfo.KEYPAD:
    868                 args.add(wps.pin);
    869                 args.add("keypad");
    870                 break;
    871             case WpsInfo.LABEL:
    872                 args.add(wps.pin);
    873                 args.add("label");
    874             default:
    875                 break;
    876         }
    877 
    878         if (config.netId == WifiP2pGroup.PERSISTENT_NET_ID) {
    879             args.add("persistent");
    880         }
    881 
    882         if (joinExistingGroup) {
    883             args.add("join");
    884         } else {
    885             //TODO: This can be adapted based on device plugged in state and
    886             //device battery state
    887             int groupOwnerIntent = config.groupOwnerIntent;
    888             if (groupOwnerIntent < 0 || groupOwnerIntent > 15) {
    889                 groupOwnerIntent = DEFAULT_GROUP_OWNER_INTENT;
    890             }
    891             args.add("go_intent=" + groupOwnerIntent);
    892         }
    893 
    894         String command = "P2P_CONNECT ";
    895         for (String s : args) command += s + " ";
    896 
    897         return doStringCommand(command);
    898     }
    899 
    900     public boolean p2pCancelConnect() {
    901         return doBooleanCommand("P2P_CANCEL");
    902     }
    903 
    904     public boolean p2pProvisionDiscovery(WifiP2pConfig config) {
    905         if (config == null) return false;
    906 
    907         switch (config.wps.setup) {
    908             case WpsInfo.PBC:
    909                 return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " pbc");
    910             case WpsInfo.DISPLAY:
    911                 //We are doing display, so provision discovery is keypad
    912                 return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " keypad");
    913             case WpsInfo.KEYPAD:
    914                 //We are doing keypad, so provision discovery is display
    915                 return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " display");
    916             default:
    917                 break;
    918         }
    919         return false;
    920     }
    921 
    922     public boolean p2pGroupAdd(boolean persistent) {
    923         if (persistent) {
    924             return doBooleanCommand("P2P_GROUP_ADD persistent");
    925         }
    926         return doBooleanCommand("P2P_GROUP_ADD");
    927     }
    928 
    929     public boolean p2pGroupAdd(int netId) {
    930         return doBooleanCommand("P2P_GROUP_ADD persistent=" + netId);
    931     }
    932 
    933     public boolean p2pGroupRemove(String iface) {
    934         if (TextUtils.isEmpty(iface)) return false;
    935         synchronized (mLock) {
    936             return doBooleanCommandNative("IFNAME=" + iface + " P2P_GROUP_REMOVE " + iface);
    937         }
    938     }
    939 
    940     public boolean p2pReject(String deviceAddress) {
    941         return doBooleanCommand("P2P_REJECT " + deviceAddress);
    942     }
    943 
    944     /* Invite a peer to a group */
    945     public boolean p2pInvite(WifiP2pGroup group, String deviceAddress) {
    946         if (TextUtils.isEmpty(deviceAddress)) return false;
    947 
    948         if (group == null) {
    949             return doBooleanCommand("P2P_INVITE peer=" + deviceAddress);
    950         } else {
    951             return doBooleanCommand("P2P_INVITE group=" + group.getInterface()
    952                     + " peer=" + deviceAddress + " go_dev_addr=" + group.getOwner().deviceAddress);
    953         }
    954     }
    955 
    956     /* Reinvoke a persistent connection */
    957     public boolean p2pReinvoke(int netId, String deviceAddress) {
    958         if (TextUtils.isEmpty(deviceAddress) || netId < 0) return false;
    959 
    960         return doBooleanCommand("P2P_INVITE persistent=" + netId + " peer=" + deviceAddress);
    961     }
    962 
    963     public String p2pGetSsid(String deviceAddress) {
    964         return p2pGetParam(deviceAddress, "oper_ssid");
    965     }
    966 
    967     public String p2pGetDeviceAddress() {
    968 
    969         Log.d(TAG, "p2pGetDeviceAddress");
    970 
    971         String status = null;
    972 
    973         /* Explicitly calling the API without IFNAME= prefix to take care of the devices that
    974         don't have p2p0 interface. Supplicant seems to be returning the correct address anyway. */
    975 
    976         synchronized (mLock) {
    977             status = doStringCommandNative("STATUS");
    978         }
    979 
    980         String result = "";
    981         if (status != null) {
    982             String[] tokens = status.split("\n");
    983             for (String token : tokens) {
    984                 if (token.startsWith("p2p_device_address=")) {
    985                     String[] nameValue = token.split("=");
    986                     if (nameValue.length != 2)
    987                         break;
    988                     result = nameValue[1];
    989                 }
    990             }
    991         }
    992 
    993         Log.d(TAG, "p2pGetDeviceAddress returning " + result);
    994         return result;
    995     }
    996 
    997     public int getGroupCapability(String deviceAddress) {
    998         int gc = 0;
    999         if (TextUtils.isEmpty(deviceAddress)) return gc;
   1000         String peerInfo = p2pPeer(deviceAddress);
   1001         if (TextUtils.isEmpty(peerInfo)) return gc;
   1002 
   1003         String[] tokens = peerInfo.split("\n");
   1004         for (String token : tokens) {
   1005             if (token.startsWith("group_capab=")) {
   1006                 String[] nameValue = token.split("=");
   1007                 if (nameValue.length != 2) break;
   1008                 try {
   1009                     return Integer.decode(nameValue[1]);
   1010                 } catch(NumberFormatException e) {
   1011                     return gc;
   1012                 }
   1013             }
   1014         }
   1015         return gc;
   1016     }
   1017 
   1018     public String p2pPeer(String deviceAddress) {
   1019         return doStringCommand("P2P_PEER " + deviceAddress);
   1020     }
   1021 
   1022     private String p2pGetParam(String deviceAddress, String key) {
   1023         if (deviceAddress == null) return null;
   1024 
   1025         String peerInfo = p2pPeer(deviceAddress);
   1026         if (peerInfo == null) return null;
   1027         String[] tokens= peerInfo.split("\n");
   1028 
   1029         key += "=";
   1030         for (String token : tokens) {
   1031             if (token.startsWith(key)) {
   1032                 String[] nameValue = token.split("=");
   1033                 if (nameValue.length != 2) break;
   1034                 return nameValue[1];
   1035             }
   1036         }
   1037         return null;
   1038     }
   1039 
   1040     public boolean p2pServiceAdd(WifiP2pServiceInfo servInfo) {
   1041         /*
   1042          * P2P_SERVICE_ADD bonjour <query hexdump> <RDATA hexdump>
   1043          * P2P_SERVICE_ADD upnp <version hex> <service>
   1044          *
   1045          * e.g)
   1046          * [Bonjour]
   1047          * # IP Printing over TCP (PTR) (RDATA=MyPrinter._ipp._tcp.local.)
   1048          * P2P_SERVICE_ADD bonjour 045f697070c00c000c01 094d795072696e746572c027
   1049          * # IP Printing over TCP (TXT) (RDATA=txtvers=1,pdl=application/postscript)
   1050          * P2P_SERVICE_ADD bonjour 096d797072696e746572045f697070c00c001001
   1051          *  09747874766572733d311a70646c3d6170706c69636174696f6e2f706f7374736372797074
   1052          *
   1053          * [UPnP]
   1054          * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012
   1055          * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice
   1056          * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::urn:schemas-upnp
   1057          * -org:device:InternetGatewayDevice:1
   1058          * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9322-123456789012::urn:schemas-upnp
   1059          * -org:service:ContentDirectory:2
   1060          */
   1061         for (String s : servInfo.getSupplicantQueryList()) {
   1062             String command = "P2P_SERVICE_ADD";
   1063             command += (" " + s);
   1064             if (!doBooleanCommand(command)) {
   1065                 return false;
   1066             }
   1067         }
   1068         return true;
   1069     }
   1070 
   1071     public boolean p2pServiceDel(WifiP2pServiceInfo servInfo) {
   1072         /*
   1073          * P2P_SERVICE_DEL bonjour <query hexdump>
   1074          * P2P_SERVICE_DEL upnp <version hex> <service>
   1075          */
   1076         for (String s : servInfo.getSupplicantQueryList()) {
   1077             String command = "P2P_SERVICE_DEL ";
   1078 
   1079             String[] data = s.split(" ");
   1080             if (data.length < 2) {
   1081                 return false;
   1082             }
   1083             if ("upnp".equals(data[0])) {
   1084                 command += s;
   1085             } else if ("bonjour".equals(data[0])) {
   1086                 command += data[0];
   1087                 command += (" " + data[1]);
   1088             } else {
   1089                 return false;
   1090             }
   1091             if (!doBooleanCommand(command)) {
   1092                 return false;
   1093             }
   1094         }
   1095         return true;
   1096     }
   1097 
   1098     public boolean p2pServiceFlush() {
   1099         return doBooleanCommand("P2P_SERVICE_FLUSH");
   1100     }
   1101 
   1102     public String p2pServDiscReq(String addr, String query) {
   1103         String command = "P2P_SERV_DISC_REQ";
   1104         command += (" " + addr);
   1105         command += (" " + query);
   1106 
   1107         return doStringCommand(command);
   1108     }
   1109 
   1110     public boolean p2pServDiscCancelReq(String id) {
   1111         return doBooleanCommand("P2P_SERV_DISC_CANCEL_REQ " + id);
   1112     }
   1113 
   1114     /* Set the current mode of miracast operation.
   1115      *  0 = disabled
   1116      *  1 = operating as source
   1117      *  2 = operating as sink
   1118      */
   1119     public void setMiracastMode(int mode) {
   1120         // Note: optional feature on the driver. It is ok for this to fail.
   1121         doBooleanCommand("DRIVER MIRACAST " + mode);
   1122     }
   1123 
   1124     public boolean fetchAnqp(String bssid, String subtypes) {
   1125         return doBooleanCommand("ANQP_GET " + bssid + " " + subtypes);
   1126     }
   1127 
   1128     /* WIFI HAL support */
   1129 
   1130     private static final String TAG = "WifiNative-HAL";
   1131     private static long sWifiHalHandle = 0;  /* used by JNI to save wifi_handle */
   1132     private static long[] sWifiIfaceHandles = null;  /* used by JNI to save interface handles */
   1133     private static int sWlan0Index = -1;
   1134     private static int sP2p0Index = -1;
   1135 
   1136     private static boolean sHalIsStarted = false;
   1137 
   1138     private static native boolean startHalNative();
   1139     private static native void stopHalNative();
   1140     private static native void waitForHalEventNative();
   1141 
   1142     private static class MonitorThread extends Thread {
   1143         public void run() {
   1144             Log.i(TAG, "Waiting for HAL events mWifiHalHandle=" + Long.toString(sWifiHalHandle));
   1145             waitForHalEventNative();
   1146         }
   1147     }
   1148 
   1149     synchronized public static boolean startHal() {
   1150         Log.i(TAG, "startHal");
   1151         synchronized (mLock) {
   1152             if (sHalIsStarted)
   1153                 return true;
   1154             if (startHalNative()) {
   1155                 getInterfaces();
   1156                 new MonitorThread().start();
   1157                 sHalIsStarted = true;
   1158                 return true;
   1159             } else {
   1160                 Log.i(TAG, "Could not start hal");
   1161                 sHalIsStarted = false;
   1162                 return false;
   1163             }
   1164         }
   1165     }
   1166 
   1167     synchronized public static void stopHal() {
   1168         stopHalNative();
   1169     }
   1170 
   1171     private static native int getInterfacesNative();
   1172 
   1173     synchronized public static int getInterfaces() {
   1174         synchronized (mLock) {
   1175             if (sWifiIfaceHandles == null) {
   1176                 int num = getInterfacesNative();
   1177                 int wifi_num = 0;
   1178                 for (int i = 0; i < num; i++) {
   1179                     String name = getInterfaceNameNative(i);
   1180                     Log.i(TAG, "interface[" + i + "] = " + name);
   1181                     if (name.equals("wlan0")) {
   1182                         sWlan0Index = i;
   1183                         wifi_num++;
   1184                     } else if (name.equals("p2p0")) {
   1185                         sP2p0Index = i;
   1186                         wifi_num++;
   1187                     }
   1188                 }
   1189                 return wifi_num;
   1190             } else {
   1191                 return sWifiIfaceHandles.length;
   1192             }
   1193         }
   1194     }
   1195 
   1196     private static native String getInterfaceNameNative(int index);
   1197     synchronized public static String getInterfaceName(int index) {
   1198         return getInterfaceNameNative(index);
   1199     }
   1200 
   1201     public static class ScanCapabilities {
   1202         public int  max_scan_cache_size;                 // in number of scan results??
   1203         public int  max_scan_buckets;
   1204         public int  max_ap_cache_per_scan;
   1205         public int  max_rssi_sample_size;
   1206         public int  max_scan_reporting_threshold;        // in number of scan results??
   1207         public int  max_hotlist_aps;
   1208         public int  max_significant_wifi_change_aps;
   1209     }
   1210 
   1211     public static boolean getScanCapabilities(ScanCapabilities capabilities) {
   1212         return getScanCapabilitiesNative(sWlan0Index, capabilities);
   1213     }
   1214 
   1215     private static native boolean getScanCapabilitiesNative(
   1216             int iface, ScanCapabilities capabilities);
   1217 
   1218     private static native boolean startScanNative(int iface, int id, ScanSettings settings);
   1219     private static native boolean stopScanNative(int iface, int id);
   1220     private static native ScanResult[] getScanResultsNative(int iface, boolean flush);
   1221     private static native WifiLinkLayerStats getWifiLinkLayerStatsNative(int iface);
   1222 
   1223     public static class ChannelSettings {
   1224         int frequency;
   1225         int dwell_time_ms;
   1226         boolean passive;
   1227     }
   1228 
   1229     public static class BucketSettings {
   1230         int bucket;
   1231         int band;
   1232         int period_ms;
   1233         int report_events;
   1234         int num_channels;
   1235         ChannelSettings channels[];
   1236     }
   1237 
   1238     public static class ScanSettings {
   1239         int base_period_ms;
   1240         int max_ap_per_scan;
   1241         int report_threshold;
   1242         int num_buckets;
   1243         BucketSettings buckets[];
   1244     }
   1245 
   1246     public static interface ScanEventHandler {
   1247         void onScanResultsAvailable();
   1248         void onFullScanResult(ScanResult fullScanResult);
   1249         void onSingleScanComplete();
   1250         void onScanPaused();
   1251         void onScanRestarted();
   1252     }
   1253 
   1254     synchronized static void onScanResultsAvailable(int id) {
   1255         if (sScanEventHandler  != null) {
   1256             sScanEventHandler.onScanResultsAvailable();
   1257         }
   1258     }
   1259 
   1260     /* scan status, keep these values in sync with gscan.h */
   1261     private static int WIFI_SCAN_BUFFER_FULL = 0;
   1262     private static int WIFI_SCAN_COMPLETE = 1;
   1263 
   1264     synchronized static void onScanStatus(int status) {
   1265         Log.i(TAG, "Got a scan status changed event, status = " + status);
   1266 
   1267         if (status == WIFI_SCAN_BUFFER_FULL) {
   1268             /* we have a separate event to take care of this */
   1269         } else if (status == WIFI_SCAN_COMPLETE) {
   1270             if (sScanEventHandler  != null) {
   1271                 sScanEventHandler.onSingleScanComplete();
   1272             }
   1273         }
   1274     }
   1275 
   1276     synchronized static void onFullScanResult(int id, ScanResult result, byte bytes[]) {
   1277         if (DBG) Log.i(TAG, "Got a full scan results event, ssid = " + result.SSID + ", " +
   1278                 "num = " + bytes.length);
   1279 
   1280         int num = 0;
   1281         for (int i = 0; i < bytes.length; ) {
   1282             num++;
   1283             int type  = (int) bytes[i] & 0xFF;
   1284             int len = (int) bytes[i + 1] & 0xFF;
   1285             if (len < 0) {
   1286                 Log.e(TAG, "bad length; returning");
   1287                 return;
   1288             }
   1289             i += len + 2;
   1290             if (DBG) Log.i(TAG, "bytes[" + i + "] = [" + type + ", " + len + "]" + ", " +
   1291                     "next = " + i);
   1292         }
   1293 
   1294         ScanResult.InformationElement elements[] = new ScanResult.InformationElement[num];
   1295         for (int i = 0, index = 0; i < num; i++) {
   1296             int type  = (int) bytes[index] & 0xFF;
   1297             int len = (int) bytes[index + 1] & 0xFF;
   1298             if (DBG) Log.i(TAG, "index = " + index + ", type = " + type + ", len = " + len);
   1299             ScanResult.InformationElement elem = new ScanResult.InformationElement();
   1300             elem.id = type;
   1301             elem.bytes = new byte[len];
   1302             for (int j = 0; j < len; j++) {
   1303                 elem.bytes[j] = bytes[index + j + 2];
   1304             }
   1305             elements[i] = elem;
   1306             index += (len + 2);
   1307         }
   1308 
   1309         result.informationElements = elements;
   1310         if (sScanEventHandler  != null) {
   1311             sScanEventHandler.onFullScanResult(result);
   1312         }
   1313     }
   1314 
   1315     private static int sScanCmdId = 0;
   1316     private static ScanEventHandler sScanEventHandler;
   1317     private static ScanSettings sScanSettings;
   1318 
   1319     synchronized public static boolean startScan(
   1320             ScanSettings settings, ScanEventHandler eventHandler) {
   1321         synchronized (mLock) {
   1322 
   1323             if (sScanCmdId != 0) {
   1324                 stopScan();
   1325             } else if (sScanSettings != null || sScanEventHandler != null) {
   1326                 /* current scan is paused; no need to stop it */
   1327             }
   1328 
   1329             sScanCmdId = getNewCmdIdLocked();
   1330 
   1331             sScanSettings = settings;
   1332             sScanEventHandler = eventHandler;
   1333 
   1334             if (startScanNative(sWlan0Index, sScanCmdId, settings) == false) {
   1335                 sScanEventHandler = null;
   1336                 sScanSettings = null;
   1337                 return false;
   1338             }
   1339 
   1340             return true;
   1341         }
   1342     }
   1343 
   1344     synchronized public static void stopScan() {
   1345         synchronized (mLock) {
   1346             stopScanNative(sWlan0Index, sScanCmdId);
   1347             sScanSettings = null;
   1348             sScanEventHandler = null;
   1349             sScanCmdId = 0;
   1350         }
   1351     }
   1352 
   1353     synchronized public static void pauseScan() {
   1354         synchronized (mLock) {
   1355             if (sScanCmdId != 0 && sScanSettings != null && sScanEventHandler != null) {
   1356                 Log.d(TAG, "Pausing scan");
   1357                 stopScanNative(sWlan0Index, sScanCmdId);
   1358                 sScanCmdId = 0;
   1359                 sScanEventHandler.onScanPaused();
   1360             }
   1361         }
   1362     }
   1363 
   1364     synchronized public static void restartScan() {
   1365         synchronized (mLock) {
   1366             if (sScanCmdId == 0 && sScanSettings != null && sScanEventHandler != null) {
   1367                 Log.d(TAG, "Restarting scan");
   1368                 startScan(sScanSettings, sScanEventHandler);
   1369                 sScanEventHandler.onScanRestarted();
   1370             }
   1371         }
   1372     }
   1373 
   1374     synchronized public static ScanResult[] getScanResults() {
   1375         synchronized (mLock) {
   1376             return getScanResultsNative(sWlan0Index, /* flush = */ false);
   1377         }
   1378     }
   1379 
   1380     public static interface HotlistEventHandler {
   1381         void onHotlistApFound (ScanResult[]result);
   1382     }
   1383 
   1384     private static int sHotlistCmdId = 0;
   1385     private static HotlistEventHandler sHotlistEventHandler;
   1386 
   1387     private native static boolean setHotlistNative(int iface, int id,
   1388             WifiScanner.HotlistSettings settings);
   1389     private native static boolean resetHotlistNative(int iface, int id);
   1390 
   1391     synchronized public static boolean setHotlist(WifiScanner.HotlistSettings settings,
   1392                                     HotlistEventHandler eventHandler) {
   1393         synchronized (mLock) {
   1394             if (sHotlistCmdId != 0) {
   1395                 return false;
   1396             } else {
   1397                 sHotlistCmdId = getNewCmdIdLocked();
   1398             }
   1399 
   1400             sHotlistEventHandler = eventHandler;
   1401             if (setHotlistNative(sWlan0Index, sScanCmdId, settings) == false) {
   1402                 sHotlistEventHandler = null;
   1403                 return false;
   1404             }
   1405 
   1406             return true;
   1407         }
   1408     }
   1409 
   1410     synchronized public static void resetHotlist() {
   1411         synchronized (mLock) {
   1412             if (sHotlistCmdId != 0) {
   1413                 resetHotlistNative(sWlan0Index, sHotlistCmdId);
   1414                 sHotlistCmdId = 0;
   1415                 sHotlistEventHandler = null;
   1416             }
   1417         }
   1418     }
   1419 
   1420     synchronized public static void onHotlistApFound(int id, ScanResult[] results) {
   1421         synchronized (mLock) {
   1422             if (sHotlistCmdId != 0) {
   1423                 sHotlistEventHandler.onHotlistApFound(results);
   1424             } else {
   1425                 /* this can happen because of race conditions */
   1426                 Log.d(TAG, "Ignoring hotlist AP found change");
   1427             }
   1428         }
   1429     }
   1430 
   1431     public static interface SignificantWifiChangeEventHandler {
   1432         void onChangesFound(ScanResult[] result);
   1433     }
   1434 
   1435     private static SignificantWifiChangeEventHandler sSignificantWifiChangeHandler;
   1436     private static int sSignificantWifiChangeCmdId;
   1437 
   1438     private static native boolean trackSignificantWifiChangeNative(
   1439             int iface, int id, WifiScanner.WifiChangeSettings settings);
   1440     private static native boolean untrackSignificantWifiChangeNative(int iface, int id);
   1441 
   1442     synchronized public static boolean trackSignificantWifiChange(
   1443             WifiScanner.WifiChangeSettings settings, SignificantWifiChangeEventHandler handler) {
   1444         synchronized (mLock) {
   1445             if (sSignificantWifiChangeCmdId != 0) {
   1446                 return false;
   1447             } else {
   1448                 sSignificantWifiChangeCmdId = getNewCmdIdLocked();
   1449             }
   1450 
   1451             sSignificantWifiChangeHandler = handler;
   1452             if (trackSignificantWifiChangeNative(sWlan0Index, sScanCmdId, settings) == false) {
   1453                 sSignificantWifiChangeHandler = null;
   1454                 return false;
   1455             }
   1456 
   1457             return true;
   1458         }
   1459     }
   1460 
   1461     synchronized static void untrackSignificantWifiChange() {
   1462         synchronized (mLock) {
   1463             if (sSignificantWifiChangeCmdId != 0) {
   1464                 untrackSignificantWifiChangeNative(sWlan0Index, sSignificantWifiChangeCmdId);
   1465                 sSignificantWifiChangeCmdId = 0;
   1466                 sSignificantWifiChangeHandler = null;
   1467             }
   1468         }
   1469     }
   1470 
   1471     synchronized static void onSignificantWifiChange(int id, ScanResult[] results) {
   1472         synchronized (mLock) {
   1473             if (sSignificantWifiChangeCmdId != 0) {
   1474                 sSignificantWifiChangeHandler.onChangesFound(results);
   1475             } else {
   1476                 /* this can happen because of race conditions */
   1477                 Log.d(TAG, "Ignoring significant wifi change");
   1478             }
   1479         }
   1480     }
   1481 
   1482     synchronized public static WifiLinkLayerStats getWifiLinkLayerStats(String iface) {
   1483         // TODO: use correct iface name to Index translation
   1484         if (iface == null) return null;
   1485         synchronized (mLock) {
   1486             if (!sHalIsStarted)
   1487                 startHal();
   1488             if (sHalIsStarted)
   1489                 return getWifiLinkLayerStatsNative(sWlan0Index);
   1490         }
   1491         return null;
   1492     }
   1493 
   1494     /*
   1495      * NFC-related calls
   1496      */
   1497     public String getNfcWpsConfigurationToken(int netId) {
   1498         return doStringCommand("WPS_NFC_CONFIG_TOKEN WPS " + netId);
   1499     }
   1500 
   1501     public String getNfcHandoverRequest() {
   1502         return doStringCommand("NFC_GET_HANDOVER_REQ NDEF P2P-CR");
   1503     }
   1504 
   1505     public String getNfcHandoverSelect() {
   1506         return doStringCommand("NFC_GET_HANDOVER_SEL NDEF P2P-CR");
   1507     }
   1508 
   1509     public boolean initiatorReportNfcHandover(String selectMessage) {
   1510         return doBooleanCommand("NFC_REPORT_HANDOVER INIT P2P 00 " + selectMessage);
   1511     }
   1512 
   1513     public boolean responderReportNfcHandover(String requestMessage) {
   1514         return doBooleanCommand("NFC_REPORT_HANDOVER RESP P2P " + requestMessage + " 00");
   1515     }
   1516 
   1517     public static native int getSupportedFeatureSetNative(int iface);
   1518     synchronized public static int getSupportedFeatureSet() {
   1519         return getSupportedFeatureSetNative(sWlan0Index);
   1520     }
   1521 
   1522     /* Rtt related commands/events */
   1523     public static interface RttEventHandler {
   1524         void onRttResults(RttManager.RttResult[] result);
   1525     }
   1526 
   1527     private static RttEventHandler sRttEventHandler;
   1528     private static int sRttCmdId;
   1529 
   1530     synchronized private static void onRttResults(int id, RttManager.RttResult[] results) {
   1531         if (id == sRttCmdId) {
   1532             Log.d(TAG, "Received " + results.length + " rtt results");
   1533             sRttEventHandler.onRttResults(results);
   1534             sRttCmdId = 0;
   1535         } else {
   1536             Log.d(TAG, "Received event for unknown cmd = " + id + ", current id = " + sRttCmdId);
   1537         }
   1538     }
   1539 
   1540     private static native boolean requestRangeNative(
   1541             int iface, int id, RttManager.RttParams[] params);
   1542     private static native boolean cancelRangeRequestNative(
   1543             int iface, int id, RttManager.RttParams[] params);
   1544 
   1545     synchronized public static boolean requestRtt(
   1546             RttManager.RttParams[] params, RttEventHandler handler) {
   1547         synchronized (mLock) {
   1548             if (sRttCmdId != 0) {
   1549                 return false;
   1550             } else {
   1551                 sRttCmdId = getNewCmdIdLocked();
   1552             }
   1553             sRttEventHandler = handler;
   1554             return requestRangeNative(sWlan0Index, sRttCmdId, params);
   1555         }
   1556     }
   1557 
   1558     synchronized public static boolean cancelRtt(RttManager.RttParams[] params) {
   1559         synchronized(mLock) {
   1560             if (sRttCmdId == 0) {
   1561                 return false;
   1562             }
   1563 
   1564             if (cancelRangeRequestNative(sWlan0Index, sRttCmdId, params)) {
   1565                 sRttEventHandler = null;
   1566                 return true;
   1567             } else {
   1568                 return false;
   1569             }
   1570         }
   1571     }
   1572 
   1573     private static native boolean setScanningMacOuiNative(int iface, byte[] oui);
   1574 
   1575     synchronized public static boolean setScanningMacOui(byte[] oui) {
   1576         synchronized (mLock) {
   1577             if (startHal()) {
   1578                 return setScanningMacOuiNative(sWlan0Index, oui);
   1579             } else {
   1580                 return false;
   1581             }
   1582         }
   1583     }
   1584 
   1585     private static native int[] getChannelsForBandNative(
   1586             int iface, int band);
   1587 
   1588     synchronized public static int [] getChannelsForBand(int band) {
   1589         synchronized (mLock) {
   1590             if (startHal()) {
   1591                 return getChannelsForBandNative(sWlan0Index, band);
   1592             } else {
   1593                 return null;
   1594             }
   1595         }
   1596     }
   1597 }
   1598