WifiNative.java revision 829909a64bdac0ea0f30907efc44e27685314225
1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wifi;
18
19import android.net.wifi.BatchedScanSettings;
20import android.net.wifi.RttManager;
21import android.net.wifi.ScanResult;
22import android.net.wifi.WifiConfiguration;
23import android.net.wifi.WifiLinkLayerStats;
24import android.net.wifi.WifiManager;
25import android.net.wifi.WifiScanner;
26import android.net.wifi.RttManager;
27import android.net.wifi.WifiSsid;
28import android.net.wifi.WpsInfo;
29import android.net.wifi.p2p.WifiP2pConfig;
30import android.net.wifi.p2p.WifiP2pGroup;
31import android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
32import android.os.SystemClock;
33import android.text.TextUtils;
34import android.util.LocalLog;
35import android.util.Log;
36
37import java.util.ArrayList;
38import java.util.List;
39import java.util.Locale;
40
41/**
42 * Native calls for bring up/shut down of the supplicant daemon and for
43 * sending requests to the supplicant daemon
44 *
45 * waitForEvent() is called on the monitor thread for events. All other methods
46 * must be serialized from the framework.
47 *
48 * {@hide}
49 */
50public class WifiNative {
51
52    private static boolean DBG = false;
53    private final String mTAG;
54    private static final int DEFAULT_GROUP_OWNER_INTENT     = 6;
55
56    static final int BLUETOOTH_COEXISTENCE_MODE_ENABLED     = 0;
57    static final int BLUETOOTH_COEXISTENCE_MODE_DISABLED    = 1;
58    static final int BLUETOOTH_COEXISTENCE_MODE_SENSE       = 2;
59
60    static final int SCAN_WITHOUT_CONNECTION_SETUP          = 1;
61    static final int SCAN_WITH_CONNECTION_SETUP             = 2;
62
63    // Hold this lock before calling supplicant - it is required to
64    // mutually exclude access from Wifi and P2p state machines
65    static final Object mLock = new Object();
66
67    public final String mInterfaceName;
68    public final String mInterfacePrefix;
69
70    private boolean mSuspendOptEnabled = false;
71
72    private static final int EID_HT_OPERATION = 61;
73    private static final int EID_VHT_OPERATION = 192;
74    private static final int EID_EXTENDED_CAPS = 127;
75    private static final int RTT_RESP_ENABLE_BIT = 70;
76    /* Register native functions */
77
78    static {
79        /* Native functions are defined in libwifi-service.so */
80        System.loadLibrary("wifi-service");
81        registerNatives();
82    }
83
84    private static native int registerNatives();
85
86    public native static boolean loadDriver();
87
88    public native static boolean isDriverLoaded();
89
90    public native static boolean unloadDriver();
91
92    public native static boolean startSupplicant(boolean p2pSupported);
93
94    /* Sends a kill signal to supplicant. To be used when we have lost connection
95       or when the supplicant is hung */
96    public native static boolean killSupplicant(boolean p2pSupported);
97
98    private native boolean connectToSupplicantNative();
99
100    private native void closeSupplicantConnectionNative();
101
102    /**
103     * Wait for the supplicant to send an event, returning the event string.
104     * @return the event string sent by the supplicant.
105     */
106    private native String waitForEventNative();
107
108    private native boolean doBooleanCommandNative(String command);
109
110    private native int doIntCommandNative(String command);
111
112    private native String doStringCommandNative(String command);
113
114    public WifiNative(String interfaceName) {
115        mInterfaceName = interfaceName;
116        mTAG = "WifiNative-" + interfaceName;
117        if (!interfaceName.equals("p2p0")) {
118            mInterfacePrefix = "IFNAME=" + interfaceName + " ";
119        } else {
120            // commands for p2p0 interface don't need prefix
121            mInterfacePrefix = "";
122        }
123    }
124
125    void enableVerboseLogging(int verbose) {
126        if (verbose > 0) {
127            DBG = true;
128        } else {
129            DBG = false;
130        }
131    }
132
133    private static final LocalLog mLocalLog = new LocalLog(1024);
134
135    // hold mLock before accessing mCmdIdLock
136    private static int sCmdId;
137
138    public LocalLog getLocalLog() {
139        return mLocalLog;
140    }
141
142    private static int getNewCmdIdLocked() {
143        return sCmdId++;
144    }
145
146    private void localLog(String s) {
147        if (mLocalLog != null)
148            mLocalLog.log(mInterfaceName + ": " + s);
149    }
150
151    public boolean connectToSupplicant() {
152        // No synchronization necessary .. it is implemented in WifiMonitor
153        localLog(mInterfacePrefix + "connectToSupplicant");
154        return connectToSupplicantNative();
155    }
156
157    public void closeSupplicantConnection() {
158        localLog(mInterfacePrefix + "closeSupplicantConnection");
159        closeSupplicantConnectionNative();
160    }
161
162    public String waitForEvent() {
163        // No synchronization necessary .. it is implemented in WifiMonitor
164        return waitForEventNative();
165    }
166
167    private boolean doBooleanCommand(String command) {
168        if (DBG) Log.d(mTAG, "doBoolean: " + command);
169        synchronized (mLock) {
170            int cmdId = getNewCmdIdLocked();
171            String toLog = Integer.toString(cmdId) + ":" + mInterfacePrefix + command;
172            boolean result = doBooleanCommandNative(mInterfacePrefix + command);
173            localLog(toLog + " -> " + result);
174            if (DBG) Log.d(mTAG, command + ": returned " + result);
175            return result;
176        }
177    }
178
179    private int doIntCommand(String command) {
180        if (DBG) Log.d(mTAG, "doInt: " + command);
181        synchronized (mLock) {
182            int cmdId = getNewCmdIdLocked();
183            String toLog = Integer.toString(cmdId) + ":" + mInterfacePrefix + command;
184            int result = doIntCommandNative(mInterfacePrefix + command);
185            localLog(toLog + " -> " + result);
186            if (DBG) Log.d(mTAG, "   returned " + result);
187            return result;
188        }
189    }
190
191    private String doStringCommand(String command) {
192        if (DBG) {
193            //GET_NETWORK commands flood the logs
194            if (!command.startsWith("GET_NETWORK")) {
195                Log.d(mTAG, "doString: [" + command + "]");
196            }
197        }
198        synchronized (mLock) {
199            int cmdId = getNewCmdIdLocked();
200            String toLog = Integer.toString(cmdId) + ":" + mInterfacePrefix + command;
201            String result = doStringCommandNative(mInterfacePrefix + command);
202            if (result == null) {
203                if (DBG) Log.d(mTAG, "doStringCommandNative no result");
204            } else {
205                if (!command.startsWith("STATUS-")) {
206                    localLog(toLog + " -> " + result);
207                }
208                if (DBG) Log.d(mTAG, "   returned " + result.replace("\n", " "));
209            }
210            return result;
211        }
212    }
213
214    private String doStringCommandWithoutLogging(String command) {
215        if (DBG) {
216            //GET_NETWORK commands flood the logs
217            if (!command.startsWith("GET_NETWORK")) {
218                Log.d(mTAG, "doString: [" + command + "]");
219            }
220        }
221        synchronized (mLock) {
222            return doStringCommandNative(mInterfacePrefix + command);
223        }
224    }
225
226    public boolean ping() {
227        String pong = doStringCommand("PING");
228        return (pong != null && pong.equals("PONG"));
229    }
230
231    public void setSupplicantLogLevel(String level) {
232        doStringCommand("LOG_LEVEL " + level);
233    }
234
235    public String getFreqCapability() {
236        return doStringCommand("GET_CAPABILITY freq");
237    }
238
239    public boolean scan(int type, String freqList) {
240        if (type == SCAN_WITHOUT_CONNECTION_SETUP) {
241            if (freqList == null) return doBooleanCommand("SCAN TYPE=ONLY");
242            else return doBooleanCommand("SCAN TYPE=ONLY freq=" + freqList);
243        } else if (type == SCAN_WITH_CONNECTION_SETUP) {
244            if (freqList == null) return doBooleanCommand("SCAN");
245            else return doBooleanCommand("SCAN freq=" + freqList);
246        } else {
247            throw new IllegalArgumentException("Invalid scan type");
248        }
249    }
250
251    /* Does a graceful shutdown of supplicant. Is a common stop function for both p2p and sta.
252     *
253     * Note that underneath we use a harsh-sounding "terminate" supplicant command
254     * for a graceful stop and a mild-sounding "stop" interface
255     * to kill the process
256     */
257    public boolean stopSupplicant() {
258        return doBooleanCommand("TERMINATE");
259    }
260
261    public String listNetworks() {
262        return doStringCommand("LIST_NETWORKS");
263    }
264
265    public String listNetworks(int last_id) {
266        return doStringCommand("LIST_NETWORKS LAST_ID=" + last_id);
267    }
268
269    public int addNetwork() {
270        return doIntCommand("ADD_NETWORK");
271    }
272
273    public boolean setNetworkVariable(int netId, String name, String value) {
274        if (TextUtils.isEmpty(name) || TextUtils.isEmpty(value)) return false;
275        return doBooleanCommand("SET_NETWORK " + netId + " " + name + " " + value);
276    }
277
278    public String getNetworkVariable(int netId, String name) {
279        if (TextUtils.isEmpty(name)) return null;
280
281        // GET_NETWORK will likely flood the logs ...
282        return doStringCommandWithoutLogging("GET_NETWORK " + netId + " " + name);
283    }
284
285    public boolean removeNetwork(int netId) {
286        return doBooleanCommand("REMOVE_NETWORK " + netId);
287    }
288
289
290    private void logDbg(String debug) {
291        long now = SystemClock.elapsedRealtimeNanos();
292        String ts = String.format("[%,d us] ", now/1000);
293        Log.e("WifiNative: ", ts+debug+ " stack:"
294                + Thread.currentThread().getStackTrace()[2].getMethodName() +" - "
295                + Thread.currentThread().getStackTrace()[3].getMethodName() +" - "
296                + Thread.currentThread().getStackTrace()[4].getMethodName() +" - "
297                + Thread.currentThread().getStackTrace()[5].getMethodName()+" - "
298                + Thread.currentThread().getStackTrace()[6].getMethodName());
299
300    }
301    public boolean enableNetwork(int netId, boolean disableOthers) {
302        if (DBG) logDbg("enableNetwork nid=" + Integer.toString(netId)
303                + " disableOthers=" + disableOthers);
304        if (disableOthers) {
305            return doBooleanCommand("SELECT_NETWORK " + netId);
306        } else {
307            return doBooleanCommand("ENABLE_NETWORK " + netId);
308        }
309    }
310
311    public boolean disableNetwork(int netId) {
312        if (DBG) logDbg("disableNetwork nid=" + Integer.toString(netId));
313        return doBooleanCommand("DISABLE_NETWORK " + netId);
314    }
315
316    public boolean selectNetwork(int netId) {
317        if (DBG) logDbg("selectNetwork nid=" + Integer.toString(netId));
318        return doBooleanCommand("SELECT_NETWORK " + netId);
319    }
320
321    public boolean reconnect() {
322        if (DBG) logDbg("RECONNECT ");
323        return doBooleanCommand("RECONNECT");
324    }
325
326    public boolean reassociate() {
327        if (DBG) logDbg("REASSOCIATE ");
328        return doBooleanCommand("REASSOCIATE");
329    }
330
331    public boolean disconnect() {
332        if (DBG) logDbg("DISCONNECT ");
333        return doBooleanCommand("DISCONNECT");
334    }
335
336    public String status() {
337        return status(false);
338    }
339
340    public String status(boolean noEvents) {
341        if (noEvents) {
342            return doStringCommand("STATUS-NO_EVENTS");
343        } else {
344            return doStringCommand("STATUS");
345        }
346    }
347
348    public String getMacAddress() {
349        //Macaddr = XX.XX.XX.XX.XX.XX
350        String ret = doStringCommand("DRIVER MACADDR");
351        if (!TextUtils.isEmpty(ret)) {
352            String[] tokens = ret.split(" = ");
353            if (tokens.length == 2) return tokens[1];
354        }
355        return null;
356    }
357
358
359
360    /**
361     * Format of results:
362     * =================
363     * id=1
364     * bssid=68:7f:74:d7:1b:6e
365     * freq=2412
366     * level=-43
367     * tsf=1344621975160944
368     * age=2623
369     * flags=[WPA2-PSK-CCMP][WPS][ESS]
370     * ssid=zubyb
371     * ====
372     *
373     * RANGE=ALL gets all scan results
374     * RANGE=ID- gets results from ID
375     * MASK=<N> see wpa_supplicant/src/common/wpa_ctrl.h for details
376     * 0                         0                        1                       0     2
377     *                           WPA_BSS_MASK_MESH_SCAN | WPA_BSS_MASK_DELIM    | WPA_BSS_MASK_WIFI_DISPLAY
378     * 0                         0                        0                       1     1   -> 9
379     * WPA_BSS_MASK_INTERNETW  | WPA_BSS_MASK_P2P_SCAN  | WPA_BSS_MASK_WPS_SCAN | WPA_BSS_MASK_SSID
380     * 1                         0                        0                       1     9   -> d
381     * WPA_BSS_MASK_FLAGS      | WPA_BSS_MASK_IE        | WPA_BSS_MASK_AGE      | WPA_BSS_MASK_TSF
382     * 1                         0                        0                       0     8
383     * WPA_BSS_MASK_LEVEL      | WPA_BSS_MASK_NOISE     | WPA_BSS_MASK_QUAL     | WPA_BSS_MASK_CAPABILITIES
384     * 0                         1                        1                       1     7
385     * WPA_BSS_MASK_BEACON_INT | WPA_BSS_MASK_FREQ      | WPA_BSS_MASK_BSSID    | WPA_BSS_MASK_ID
386     *
387     * WPA_BSS_MASK_INTERNETW adds ANQP info (ctrl_iface:4151-4176)
388     *
389     * ctrl_iface.c:wpa_supplicant_ctrl_iface_process:7884
390     *  wpa_supplicant_ctrl_iface_bss:4315
391     *  print_bss_info
392     */
393    public String scanResults(int sid) {
394        return doStringCommandWithoutLogging("BSS RANGE=" + sid + "- MASK=0x29d87");
395    }
396
397    public String doCustomCommand(String command) {
398        return doStringCommand(command);
399    }
400
401    /**
402     * Format of result:
403     * id=1016
404     * bssid=00:03:7f:40:84:10
405     * freq=2462
406     * beacon_int=200
407     * capabilities=0x0431
408     * qual=0
409     * noise=0
410     * level=-46
411     * tsf=0000002669008476
412     * age=5
413     * ie=00105143412d485332302d52322d54455354010882848b960c12182403010b0706555...
414     * flags=[WPA2-EAP-CCMP][ESS][P2P][HS20]
415     * ssid=QCA-HS20-R2-TEST
416     * p2p_device_name=
417     * p2p_config_methods=0x0SET_NE
418     * anqp_venue_name=02083d656e6757692d466920416c6c69616e63650a3239383920436f...
419     * anqp_network_auth_type=010000
420     * anqp_roaming_consortium=03506f9a05001bc504bd
421     * anqp_ip_addr_type_availability=0c
422     * anqp_nai_realm=0200300000246d61696c2e6578616d706c652e636f6d3b636973636f2...
423     * anqp_3gpp=000600040132f465
424     * anqp_domain_name=0b65786d61706c652e636f6d
425     * hs20_operator_friendly_name=11656e6757692d466920416c6c69616e63650e636869...
426     * hs20_wan_metrics=01c40900008001000000000a00
427     * hs20_connection_capability=0100000006140001061600000650000106bb010106bb0...
428     * hs20_osu_providers_list=0b5143412d4f53552d425353010901310015656e6757692d...
429     */
430    public String scanResult(String bssid) {
431        return doStringCommand("BSS " + bssid);
432    }
433
434    /**
435     * Format of command
436     * DRIVER WLS_BATCHING SET SCANFREQ=x MSCAN=r BESTN=y CHANNEL=<z, w, t> RTT=s
437     * where x is an ascii representation of an integer number of seconds between scans
438     *       r is an ascii representation of an integer number of scans per batch
439     *       y is an ascii representation of an integer number of the max AP to remember per scan
440     *       z, w, t represent a 1..n size list of channel numbers and/or 'A', 'B' values
441     *           indicating entire ranges of channels
442     *       s is an ascii representation of an integer number of highest-strength AP
443     *           for which we'd like approximate distance reported
444     *
445     * The return value is an ascii integer representing a guess of the number of scans
446     * the firmware can remember before it runs out of buffer space or -1 on error
447     */
448    public String setBatchedScanSettings(BatchedScanSettings settings) {
449        if (settings == null) {
450            return doStringCommand("DRIVER WLS_BATCHING STOP");
451        }
452        String cmd = "DRIVER WLS_BATCHING SET SCANFREQ=" + settings.scanIntervalSec;
453        cmd += " MSCAN=" + settings.maxScansPerBatch;
454        if (settings.maxApPerScan != BatchedScanSettings.UNSPECIFIED) {
455            cmd += " BESTN=" + settings.maxApPerScan;
456        }
457        if (settings.channelSet != null && !settings.channelSet.isEmpty()) {
458            cmd += " CHANNEL=<";
459            int i = 0;
460            for (String channel : settings.channelSet) {
461                cmd += (i > 0 ? "," : "") + channel;
462                ++i;
463            }
464            cmd += ">";
465        }
466        if (settings.maxApForDistance != BatchedScanSettings.UNSPECIFIED) {
467            cmd += " RTT=" + settings.maxApForDistance;
468        }
469        return doStringCommand(cmd);
470    }
471
472    public String getBatchedScanResults() {
473        return doStringCommand("DRIVER WLS_BATCHING GET");
474    }
475
476    public boolean startDriver() {
477        return doBooleanCommand("DRIVER START");
478    }
479
480    public boolean stopDriver() {
481        return doBooleanCommand("DRIVER STOP");
482    }
483
484
485    /**
486     * Start filtering out Multicast V4 packets
487     * @return {@code true} if the operation succeeded, {@code false} otherwise
488     *
489     * Multicast filtering rules work as follows:
490     *
491     * The driver can filter multicast (v4 and/or v6) and broadcast packets when in
492     * a power optimized mode (typically when screen goes off).
493     *
494     * In order to prevent the driver from filtering the multicast/broadcast packets, we have to
495     * add a DRIVER RXFILTER-ADD rule followed by DRIVER RXFILTER-START to make the rule effective
496     *
497     * DRIVER RXFILTER-ADD Num
498     *   where Num = 0 - Unicast, 1 - Broadcast, 2 - Mutil4 or 3 - Multi6
499     *
500     * and DRIVER RXFILTER-START
501     * In order to stop the usage of these rules, we do
502     *
503     * DRIVER RXFILTER-STOP
504     * DRIVER RXFILTER-REMOVE Num
505     *   where Num is as described for RXFILTER-ADD
506     *
507     * The  SETSUSPENDOPT driver command overrides the filtering rules
508     */
509    public boolean startFilteringMulticastV4Packets() {
510        return doBooleanCommand("DRIVER RXFILTER-STOP")
511            && doBooleanCommand("DRIVER RXFILTER-REMOVE 2")
512            && doBooleanCommand("DRIVER RXFILTER-START");
513    }
514
515    /**
516     * Stop filtering out Multicast V4 packets.
517     * @return {@code true} if the operation succeeded, {@code false} otherwise
518     */
519    public boolean stopFilteringMulticastV4Packets() {
520        return doBooleanCommand("DRIVER RXFILTER-STOP")
521            && doBooleanCommand("DRIVER RXFILTER-ADD 2")
522            && doBooleanCommand("DRIVER RXFILTER-START");
523    }
524
525    /**
526     * Start filtering out Multicast V6 packets
527     * @return {@code true} if the operation succeeded, {@code false} otherwise
528     */
529    public boolean startFilteringMulticastV6Packets() {
530        return doBooleanCommand("DRIVER RXFILTER-STOP")
531            && doBooleanCommand("DRIVER RXFILTER-REMOVE 3")
532            && doBooleanCommand("DRIVER RXFILTER-START");
533    }
534
535    /**
536     * Stop filtering out Multicast V6 packets.
537     * @return {@code true} if the operation succeeded, {@code false} otherwise
538     */
539    public boolean stopFilteringMulticastV6Packets() {
540        return doBooleanCommand("DRIVER RXFILTER-STOP")
541            && doBooleanCommand("DRIVER RXFILTER-ADD 3")
542            && doBooleanCommand("DRIVER RXFILTER-START");
543    }
544
545    /**
546     * Set the operational frequency band
547     * @param band One of
548     *     {@link WifiManager#WIFI_FREQUENCY_BAND_AUTO},
549     *     {@link WifiManager#WIFI_FREQUENCY_BAND_5GHZ},
550     *     {@link WifiManager#WIFI_FREQUENCY_BAND_2GHZ},
551     * @return {@code true} if the operation succeeded, {@code false} otherwise
552     */
553    public boolean setBand(int band) {
554        String bandstr;
555
556        if (band == WifiManager.WIFI_FREQUENCY_BAND_5GHZ)
557            bandstr = "5G";
558        else if (band == WifiManager.WIFI_FREQUENCY_BAND_2GHZ)
559            bandstr = "2G";
560        else
561            bandstr = "AUTO";
562        return doBooleanCommand("SET SETBAND " + bandstr);
563    }
564
565    /**
566      * Sets the bluetooth coexistence mode.
567      *
568      * @param mode One of {@link #BLUETOOTH_COEXISTENCE_MODE_DISABLED},
569      *            {@link #BLUETOOTH_COEXISTENCE_MODE_ENABLED}, or
570      *            {@link #BLUETOOTH_COEXISTENCE_MODE_SENSE}.
571      * @return Whether the mode was successfully set.
572      */
573    public boolean setBluetoothCoexistenceMode(int mode) {
574        return doBooleanCommand("DRIVER BTCOEXMODE " + mode);
575    }
576
577    /**
578     * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
579     * some of the low-level scan parameters used by the driver are changed to
580     * reduce interference with A2DP streaming.
581     *
582     * @param isSet whether to enable or disable this mode
583     * @return {@code true} if the command succeeded, {@code false} otherwise.
584     */
585    public boolean setBluetoothCoexistenceScanMode(boolean setCoexScanMode) {
586        if (setCoexScanMode) {
587            return doBooleanCommand("DRIVER BTCOEXSCAN-START");
588        } else {
589            return doBooleanCommand("DRIVER BTCOEXSCAN-STOP");
590        }
591    }
592
593    public void enableSaveConfig() {
594        doBooleanCommand("SET update_config 1");
595    }
596
597    public boolean saveConfig() {
598        return doBooleanCommand("SAVE_CONFIG");
599    }
600
601    public boolean addToBlacklist(String bssid) {
602        if (TextUtils.isEmpty(bssid)) return false;
603        return doBooleanCommand("BLACKLIST " + bssid);
604    }
605
606    public boolean clearBlacklist() {
607        return doBooleanCommand("BLACKLIST clear");
608    }
609
610    public boolean setSuspendOptimizations(boolean enabled) {
611       // if (mSuspendOptEnabled == enabled) return true;
612        mSuspendOptEnabled = enabled;
613
614        Log.e("native", "do suspend " + enabled);
615        if (enabled) {
616            return doBooleanCommand("DRIVER SETSUSPENDMODE 1");
617        } else {
618            return doBooleanCommand("DRIVER SETSUSPENDMODE 0");
619        }
620    }
621
622    public boolean setCountryCode(String countryCode) {
623        if (countryCode != null)
624            return doBooleanCommand("DRIVER COUNTRY " + countryCode.toUpperCase(Locale.ROOT));
625        else
626            return doBooleanCommand("DRIVER COUNTRY");
627    }
628
629    public void enableBackgroundScan(boolean enable) {
630        if (enable) {
631            doBooleanCommand("SET pno 1");
632        } else {
633            doBooleanCommand("SET pno 0");
634        }
635    }
636
637    public void enableAutoConnect(boolean enable) {
638        if (enable) {
639            doBooleanCommand("STA_AUTOCONNECT 1");
640        } else {
641            doBooleanCommand("STA_AUTOCONNECT 0");
642        }
643    }
644
645    public void setScanInterval(int scanInterval) {
646        doBooleanCommand("SCAN_INTERVAL " + scanInterval);
647    }
648
649    public void startTdls(String macAddr, boolean enable) {
650        if (enable) {
651            doBooleanCommand("TDLS_DISCOVER " + macAddr);
652            doBooleanCommand("TDLS_SETUP " + macAddr);
653        } else {
654            doBooleanCommand("TDLS_TEARDOWN " + macAddr);
655        }
656    }
657
658    /** Example output:
659     * RSSI=-65
660     * LINKSPEED=48
661     * NOISE=9999
662     * FREQUENCY=0
663     */
664    public String signalPoll() {
665        return doStringCommandWithoutLogging("SIGNAL_POLL");
666    }
667
668    /** Example outout:
669     * TXGOOD=396
670     * TXBAD=1
671     */
672    public String pktcntPoll() {
673        return doStringCommand("PKTCNT_POLL");
674    }
675
676    public void bssFlush() {
677        doBooleanCommand("BSS_FLUSH 0");
678    }
679
680    public boolean startWpsPbc(String bssid) {
681        if (TextUtils.isEmpty(bssid)) {
682            return doBooleanCommand("WPS_PBC");
683        } else {
684            return doBooleanCommand("WPS_PBC " + bssid);
685        }
686    }
687
688    public boolean startWpsPbc(String iface, String bssid) {
689        synchronized (mLock) {
690            if (TextUtils.isEmpty(bssid)) {
691                return doBooleanCommandNative("IFNAME=" + iface + " WPS_PBC");
692            } else {
693                return doBooleanCommandNative("IFNAME=" + iface + " WPS_PBC " + bssid);
694            }
695        }
696    }
697
698    public boolean startWpsPinKeypad(String pin) {
699        if (TextUtils.isEmpty(pin)) return false;
700        return doBooleanCommand("WPS_PIN any " + pin);
701    }
702
703    public boolean startWpsPinKeypad(String iface, String pin) {
704        if (TextUtils.isEmpty(pin)) return false;
705        synchronized (mLock) {
706            return doBooleanCommandNative("IFNAME=" + iface + " WPS_PIN any " + pin);
707        }
708    }
709
710
711    public String startWpsPinDisplay(String bssid) {
712        if (TextUtils.isEmpty(bssid)) {
713            return doStringCommand("WPS_PIN any");
714        } else {
715            return doStringCommand("WPS_PIN " + bssid);
716        }
717    }
718
719    public String startWpsPinDisplay(String iface, String bssid) {
720        synchronized (mLock) {
721            if (TextUtils.isEmpty(bssid)) {
722                return doStringCommandNative("IFNAME=" + iface + " WPS_PIN any");
723            } else {
724                return doStringCommandNative("IFNAME=" + iface + " WPS_PIN " + bssid);
725            }
726        }
727    }
728
729    public boolean setExternalSim(boolean external) {
730        synchronized (mLock) {
731            String value = external ? "1" : "0";
732            Log.d(TAG, "Setting external_sim to " + value);
733            return doBooleanCommand("SET external_sim " + value);
734        }
735    }
736
737    public boolean simAuthResponse(int id, String type, String response) {
738        // with type = GSM-AUTH, UMTS-AUTH or UMTS-AUTS
739        synchronized (mLock) {
740            return doBooleanCommand("CTRL-RSP-SIM-" + id + ":" + type + response);
741        }
742    }
743
744    public boolean simIdentityResponse(int id, String response) {
745        synchronized (mLock) {
746            return doBooleanCommand("CTRL-RSP-IDENTITY-" + id + ":" + response);
747        }
748    }
749
750    /* Configures an access point connection */
751    public boolean startWpsRegistrar(String bssid, String pin) {
752        if (TextUtils.isEmpty(bssid) || TextUtils.isEmpty(pin)) return false;
753        return doBooleanCommand("WPS_REG " + bssid + " " + pin);
754    }
755
756    public boolean cancelWps() {
757        return doBooleanCommand("WPS_CANCEL");
758    }
759
760    public boolean setPersistentReconnect(boolean enabled) {
761        int value = (enabled == true) ? 1 : 0;
762        return doBooleanCommand("SET persistent_reconnect " + value);
763    }
764
765    public boolean setDeviceName(String name) {
766        return doBooleanCommand("SET device_name " + name);
767    }
768
769    public boolean setDeviceType(String type) {
770        return doBooleanCommand("SET device_type " + type);
771    }
772
773    public boolean setConfigMethods(String cfg) {
774        return doBooleanCommand("SET config_methods " + cfg);
775    }
776
777    public boolean setManufacturer(String value) {
778        return doBooleanCommand("SET manufacturer " + value);
779    }
780
781    public boolean setModelName(String value) {
782        return doBooleanCommand("SET model_name " + value);
783    }
784
785    public boolean setModelNumber(String value) {
786        return doBooleanCommand("SET model_number " + value);
787    }
788
789    public boolean setSerialNumber(String value) {
790        return doBooleanCommand("SET serial_number " + value);
791    }
792
793    public boolean setP2pSsidPostfix(String postfix) {
794        return doBooleanCommand("SET p2p_ssid_postfix " + postfix);
795    }
796
797    public boolean setP2pGroupIdle(String iface, int time) {
798        synchronized (mLock) {
799            return doBooleanCommandNative("IFNAME=" + iface + " SET p2p_group_idle " + time);
800        }
801    }
802
803    public void setPowerSave(boolean enabled) {
804        if (enabled) {
805            doBooleanCommand("SET ps 1");
806        } else {
807            doBooleanCommand("SET ps 0");
808        }
809    }
810
811    public boolean setP2pPowerSave(String iface, boolean enabled) {
812        synchronized (mLock) {
813            if (enabled) {
814                return doBooleanCommandNative("IFNAME=" + iface + " P2P_SET ps 1");
815            } else {
816                return doBooleanCommandNative("IFNAME=" + iface + " P2P_SET ps 0");
817            }
818        }
819    }
820
821    public boolean setWfdEnable(boolean enable) {
822        return doBooleanCommand("SET wifi_display " + (enable ? "1" : "0"));
823    }
824
825    public boolean setWfdDeviceInfo(String hex) {
826        return doBooleanCommand("WFD_SUBELEM_SET 0 " + hex);
827    }
828
829    /**
830     * "sta" prioritizes STA connection over P2P and "p2p" prioritizes
831     * P2P connection over STA
832     */
833    public boolean setConcurrencyPriority(String s) {
834        return doBooleanCommand("P2P_SET conc_pref " + s);
835    }
836
837    public boolean p2pFind() {
838        return doBooleanCommand("P2P_FIND");
839    }
840
841    public boolean p2pFind(int timeout) {
842        if (timeout <= 0) {
843            return p2pFind();
844        }
845        return doBooleanCommand("P2P_FIND " + timeout);
846    }
847
848    public boolean p2pStopFind() {
849       return doBooleanCommand("P2P_STOP_FIND");
850    }
851
852    public boolean p2pListen() {
853        return doBooleanCommand("P2P_LISTEN");
854    }
855
856    public boolean p2pListen(int timeout) {
857        if (timeout <= 0) {
858            return p2pListen();
859        }
860        return doBooleanCommand("P2P_LISTEN " + timeout);
861    }
862
863    public boolean p2pExtListen(boolean enable, int period, int interval) {
864        if (enable && interval < period) {
865            return false;
866        }
867        return doBooleanCommand("P2P_EXT_LISTEN"
868                    + (enable ? (" " + period + " " + interval) : ""));
869    }
870
871    public boolean p2pSetChannel(int lc, int oc) {
872        if (DBG) Log.d(mTAG, "p2pSetChannel: lc="+lc+", oc="+oc);
873
874        if (lc >=1 && lc <= 11) {
875            if (!doBooleanCommand("P2P_SET listen_channel " + lc)) {
876                return false;
877            }
878        } else if (lc != 0) {
879            return false;
880        }
881
882        if (oc >= 1 && oc <= 165 ) {
883            int freq = (oc <= 14 ? 2407 : 5000) + oc * 5;
884            return doBooleanCommand("P2P_SET disallow_freq 1000-"
885                    + (freq - 5) + "," + (freq + 5) + "-6000");
886        } else if (oc == 0) {
887            /* oc==0 disables "P2P_SET disallow_freq" (enables all freqs) */
888            return doBooleanCommand("P2P_SET disallow_freq \"\"");
889        }
890
891        return false;
892    }
893
894    public boolean p2pFlush() {
895        return doBooleanCommand("P2P_FLUSH");
896    }
897
898    /* p2p_connect <peer device address> <pbc|pin|PIN#> [label|display|keypad]
899        [persistent] [join|auth] [go_intent=<0..15>] [freq=<in MHz>] */
900    public String p2pConnect(WifiP2pConfig config, boolean joinExistingGroup) {
901        if (config == null) return null;
902        List<String> args = new ArrayList<String>();
903        WpsInfo wps = config.wps;
904        args.add(config.deviceAddress);
905
906        switch (wps.setup) {
907            case WpsInfo.PBC:
908                args.add("pbc");
909                break;
910            case WpsInfo.DISPLAY:
911                if (TextUtils.isEmpty(wps.pin)) {
912                    args.add("pin");
913                } else {
914                    args.add(wps.pin);
915                }
916                args.add("display");
917                break;
918            case WpsInfo.KEYPAD:
919                args.add(wps.pin);
920                args.add("keypad");
921                break;
922            case WpsInfo.LABEL:
923                args.add(wps.pin);
924                args.add("label");
925            default:
926                break;
927        }
928
929        if (config.netId == WifiP2pGroup.PERSISTENT_NET_ID) {
930            args.add("persistent");
931        }
932
933        if (joinExistingGroup) {
934            args.add("join");
935        } else {
936            //TODO: This can be adapted based on device plugged in state and
937            //device battery state
938            int groupOwnerIntent = config.groupOwnerIntent;
939            if (groupOwnerIntent < 0 || groupOwnerIntent > 15) {
940                groupOwnerIntent = DEFAULT_GROUP_OWNER_INTENT;
941            }
942            args.add("go_intent=" + groupOwnerIntent);
943        }
944
945        String command = "P2P_CONNECT ";
946        for (String s : args) command += s + " ";
947
948        return doStringCommand(command);
949    }
950
951    public boolean p2pCancelConnect() {
952        return doBooleanCommand("P2P_CANCEL");
953    }
954
955    public boolean p2pProvisionDiscovery(WifiP2pConfig config) {
956        if (config == null) return false;
957
958        switch (config.wps.setup) {
959            case WpsInfo.PBC:
960                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " pbc");
961            case WpsInfo.DISPLAY:
962                //We are doing display, so provision discovery is keypad
963                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " keypad");
964            case WpsInfo.KEYPAD:
965                //We are doing keypad, so provision discovery is display
966                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " display");
967            default:
968                break;
969        }
970        return false;
971    }
972
973    public boolean p2pGroupAdd(boolean persistent) {
974        if (persistent) {
975            return doBooleanCommand("P2P_GROUP_ADD persistent");
976        }
977        return doBooleanCommand("P2P_GROUP_ADD");
978    }
979
980    public boolean p2pGroupAdd(int netId) {
981        return doBooleanCommand("P2P_GROUP_ADD persistent=" + netId);
982    }
983
984    public boolean p2pGroupRemove(String iface) {
985        if (TextUtils.isEmpty(iface)) return false;
986        synchronized (mLock) {
987            return doBooleanCommandNative("IFNAME=" + iface + " P2P_GROUP_REMOVE " + iface);
988        }
989    }
990
991    public boolean p2pReject(String deviceAddress) {
992        return doBooleanCommand("P2P_REJECT " + deviceAddress);
993    }
994
995    /* Invite a peer to a group */
996    public boolean p2pInvite(WifiP2pGroup group, String deviceAddress) {
997        if (TextUtils.isEmpty(deviceAddress)) return false;
998
999        if (group == null) {
1000            return doBooleanCommand("P2P_INVITE peer=" + deviceAddress);
1001        } else {
1002            return doBooleanCommand("P2P_INVITE group=" + group.getInterface()
1003                    + " peer=" + deviceAddress + " go_dev_addr=" + group.getOwner().deviceAddress);
1004        }
1005    }
1006
1007    /* Reinvoke a persistent connection */
1008    public boolean p2pReinvoke(int netId, String deviceAddress) {
1009        if (TextUtils.isEmpty(deviceAddress) || netId < 0) return false;
1010
1011        return doBooleanCommand("P2P_INVITE persistent=" + netId + " peer=" + deviceAddress);
1012    }
1013
1014    public String p2pGetSsid(String deviceAddress) {
1015        return p2pGetParam(deviceAddress, "oper_ssid");
1016    }
1017
1018    public String p2pGetDeviceAddress() {
1019
1020        Log.d(TAG, "p2pGetDeviceAddress");
1021
1022        String status = null;
1023
1024        /* Explicitly calling the API without IFNAME= prefix to take care of the devices that
1025        don't have p2p0 interface. Supplicant seems to be returning the correct address anyway. */
1026
1027        synchronized (mLock) {
1028            status = doStringCommandNative("STATUS");
1029        }
1030
1031        String result = "";
1032        if (status != null) {
1033            String[] tokens = status.split("\n");
1034            for (String token : tokens) {
1035                if (token.startsWith("p2p_device_address=")) {
1036                    String[] nameValue = token.split("=");
1037                    if (nameValue.length != 2)
1038                        break;
1039                    result = nameValue[1];
1040                }
1041            }
1042        }
1043
1044        Log.d(TAG, "p2pGetDeviceAddress returning " + result);
1045        return result;
1046    }
1047
1048    public int getGroupCapability(String deviceAddress) {
1049        int gc = 0;
1050        if (TextUtils.isEmpty(deviceAddress)) return gc;
1051        String peerInfo = p2pPeer(deviceAddress);
1052        if (TextUtils.isEmpty(peerInfo)) return gc;
1053
1054        String[] tokens = peerInfo.split("\n");
1055        for (String token : tokens) {
1056            if (token.startsWith("group_capab=")) {
1057                String[] nameValue = token.split("=");
1058                if (nameValue.length != 2) break;
1059                try {
1060                    return Integer.decode(nameValue[1]);
1061                } catch(NumberFormatException e) {
1062                    return gc;
1063                }
1064            }
1065        }
1066        return gc;
1067    }
1068
1069    public String p2pPeer(String deviceAddress) {
1070        return doStringCommand("P2P_PEER " + deviceAddress);
1071    }
1072
1073    private String p2pGetParam(String deviceAddress, String key) {
1074        if (deviceAddress == null) return null;
1075
1076        String peerInfo = p2pPeer(deviceAddress);
1077        if (peerInfo == null) return null;
1078        String[] tokens= peerInfo.split("\n");
1079
1080        key += "=";
1081        for (String token : tokens) {
1082            if (token.startsWith(key)) {
1083                String[] nameValue = token.split("=");
1084                if (nameValue.length != 2) break;
1085                return nameValue[1];
1086            }
1087        }
1088        return null;
1089    }
1090
1091    public boolean p2pServiceAdd(WifiP2pServiceInfo servInfo) {
1092        /*
1093         * P2P_SERVICE_ADD bonjour <query hexdump> <RDATA hexdump>
1094         * P2P_SERVICE_ADD upnp <version hex> <service>
1095         *
1096         * e.g)
1097         * [Bonjour]
1098         * # IP Printing over TCP (PTR) (RDATA=MyPrinter._ipp._tcp.local.)
1099         * P2P_SERVICE_ADD bonjour 045f697070c00c000c01 094d795072696e746572c027
1100         * # IP Printing over TCP (TXT) (RDATA=txtvers=1,pdl=application/postscript)
1101         * P2P_SERVICE_ADD bonjour 096d797072696e746572045f697070c00c001001
1102         *  09747874766572733d311a70646c3d6170706c69636174696f6e2f706f7374736372797074
1103         *
1104         * [UPnP]
1105         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012
1106         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice
1107         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::urn:schemas-upnp
1108         * -org:device:InternetGatewayDevice:1
1109         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9322-123456789012::urn:schemas-upnp
1110         * -org:service:ContentDirectory:2
1111         */
1112        for (String s : servInfo.getSupplicantQueryList()) {
1113            String command = "P2P_SERVICE_ADD";
1114            command += (" " + s);
1115            if (!doBooleanCommand(command)) {
1116                return false;
1117            }
1118        }
1119        return true;
1120    }
1121
1122    public boolean p2pServiceDel(WifiP2pServiceInfo servInfo) {
1123        /*
1124         * P2P_SERVICE_DEL bonjour <query hexdump>
1125         * P2P_SERVICE_DEL upnp <version hex> <service>
1126         */
1127        for (String s : servInfo.getSupplicantQueryList()) {
1128            String command = "P2P_SERVICE_DEL ";
1129
1130            String[] data = s.split(" ");
1131            if (data.length < 2) {
1132                return false;
1133            }
1134            if ("upnp".equals(data[0])) {
1135                command += s;
1136            } else if ("bonjour".equals(data[0])) {
1137                command += data[0];
1138                command += (" " + data[1]);
1139            } else {
1140                return false;
1141            }
1142            if (!doBooleanCommand(command)) {
1143                return false;
1144            }
1145        }
1146        return true;
1147    }
1148
1149    public boolean p2pServiceFlush() {
1150        return doBooleanCommand("P2P_SERVICE_FLUSH");
1151    }
1152
1153    public String p2pServDiscReq(String addr, String query) {
1154        String command = "P2P_SERV_DISC_REQ";
1155        command += (" " + addr);
1156        command += (" " + query);
1157
1158        return doStringCommand(command);
1159    }
1160
1161    public boolean p2pServDiscCancelReq(String id) {
1162        return doBooleanCommand("P2P_SERV_DISC_CANCEL_REQ " + id);
1163    }
1164
1165    /* Set the current mode of miracast operation.
1166     *  0 = disabled
1167     *  1 = operating as source
1168     *  2 = operating as sink
1169     */
1170    public void setMiracastMode(int mode) {
1171        // Note: optional feature on the driver. It is ok for this to fail.
1172        doBooleanCommand("DRIVER MIRACAST " + mode);
1173    }
1174
1175    public boolean fetchAnqp(String bssid, String subtypes) {
1176        return doBooleanCommand("ANQP_GET " + bssid + " " + subtypes);
1177    }
1178
1179    /* WIFI HAL support */
1180
1181    private static final String TAG = "WifiNative-HAL";
1182    private static long sWifiHalHandle = 0;  /* used by JNI to save wifi_handle */
1183    private static long[] sWifiIfaceHandles = null;  /* used by JNI to save interface handles */
1184    private static int sWlan0Index = -1;
1185    private static int sP2p0Index = -1;
1186
1187    private static boolean sHalIsStarted = false;
1188    private static boolean sHalFailed = false;
1189
1190    private static native boolean startHalNative();
1191    private static native void stopHalNative();
1192    private static native void waitForHalEventNative();
1193
1194    private static class MonitorThread extends Thread {
1195        public void run() {
1196            Log.i(TAG, "Waiting for HAL events mWifiHalHandle=" + Long.toString(sWifiHalHandle));
1197            waitForHalEventNative();
1198        }
1199    }
1200
1201    synchronized public static boolean startHal() {
1202
1203        String debugLog = "startHal stack: ";
1204        java.lang.StackTraceElement[] elements = Thread.currentThread().getStackTrace();
1205        for (int i = 2; i < elements.length && i <= 7; i++ ) {
1206            debugLog = debugLog + " - " + elements[i].getMethodName();
1207        }
1208
1209        Log.i(TAG,debugLog);
1210
1211        synchronized (mLock) {
1212            if (sHalFailed)
1213                return false;
1214            if (startHalNative() && (getInterfaces() != 0) && (sWlan0Index != -1)) {
1215                if(!sHalIsStarted){
1216                    new MonitorThread().start();
1217                }
1218                sHalIsStarted = true;
1219                return true;
1220            } else {
1221                Log.i(TAG, "Could not start hal");
1222                sHalIsStarted = false;
1223                sHalFailed = true;
1224                return false;
1225            }
1226        }
1227    }
1228
1229    synchronized public static void stopHal() {
1230        stopHalNative();
1231    }
1232
1233    private static native int getInterfacesNative();
1234
1235    synchronized public static int getInterfaces() {
1236        synchronized (mLock) {
1237            if (sWifiIfaceHandles == null) {
1238                int num = getInterfacesNative();
1239                int wifi_num = 0;
1240                for (int i = 0; i < num; i++) {
1241                    String name = getInterfaceNameNative(i);
1242                    Log.i(TAG, "interface[" + i + "] = " + name);
1243                    if (name.equals("wlan0")) {
1244                        sWlan0Index = i;
1245                        wifi_num++;
1246                    } else if (name.equals("p2p0")) {
1247                        sP2p0Index = i;
1248                        wifi_num++;
1249                    }
1250                }
1251                return wifi_num;
1252            } else {
1253                return sWifiIfaceHandles.length;
1254            }
1255        }
1256    }
1257
1258    private static native String getInterfaceNameNative(int index);
1259    synchronized public static String getInterfaceName(int index) {
1260        return getInterfaceNameNative(index);
1261    }
1262
1263    public static class ScanCapabilities {
1264        public int  max_scan_cache_size;                 // in number of scan results??
1265        public int  max_scan_buckets;
1266        public int  max_ap_cache_per_scan;
1267        public int  max_rssi_sample_size;
1268        public int  max_scan_reporting_threshold;        // in number of scan results??
1269        public int  max_hotlist_bssids;
1270        public int  max_significant_wifi_change_aps;
1271    }
1272
1273    public static boolean getScanCapabilities(ScanCapabilities capabilities) {
1274        return getScanCapabilitiesNative(sWlan0Index, capabilities);
1275    }
1276
1277    private static native boolean getScanCapabilitiesNative(
1278            int iface, ScanCapabilities capabilities);
1279
1280    private static native boolean startScanNative(int iface, int id, ScanSettings settings);
1281    private static native boolean stopScanNative(int iface, int id);
1282    private static native WifiScanner.ScanData[] getScanResultsNative(int iface, boolean flush);
1283    private static native WifiLinkLayerStats getWifiLinkLayerStatsNative(int iface);
1284
1285    public static class ChannelSettings {
1286        int frequency;
1287        int dwell_time_ms;
1288        boolean passive;
1289    }
1290
1291    public static class BucketSettings {
1292        int bucket;
1293        int band;
1294        int period_ms;
1295        int report_events;
1296        int num_channels;
1297        ChannelSettings channels[];
1298    }
1299
1300    public static class ScanSettings {
1301        int base_period_ms;
1302        int max_ap_per_scan;
1303        int report_threshold_percent;
1304        int report_threshold_num_scans;
1305        int num_buckets;
1306        BucketSettings buckets[];
1307    }
1308
1309    public static interface ScanEventHandler {
1310        void onScanResultsAvailable();
1311        void onFullScanResult(ScanResult fullScanResult);
1312        void onScanStatus();
1313        void onScanPaused(WifiScanner.ScanData[] data);
1314        void onScanRestarted();
1315    }
1316
1317    synchronized static void onScanResultsAvailable(int id) {
1318        if (sScanEventHandler  != null) {
1319            sScanEventHandler.onScanResultsAvailable();
1320        }
1321    }
1322
1323    /* scan status, keep these values in sync with gscan.h */
1324    private static int WIFI_SCAN_BUFFER_FULL = 0;
1325    private static int WIFI_SCAN_COMPLETE = 1;
1326
1327    synchronized static void onScanStatus(int status) {
1328        Log.i(TAG, "Got a scan status changed event, status = " + status);
1329
1330        if (status == WIFI_SCAN_BUFFER_FULL) {
1331            /* we have a separate event to take care of this */
1332        } else if (status == WIFI_SCAN_COMPLETE) {
1333            if (sScanEventHandler  != null) {
1334                sScanEventHandler.onScanStatus();
1335            }
1336        }
1337    }
1338
1339    static void populateScanResult(ScanResult result, byte bytes[], String dbg) {
1340        int num = 0;
1341        if (bytes == null) return;
1342        if (dbg == null) dbg = "";
1343        for (int i = 0; i < bytes.length; ) {
1344            int type  = bytes[i] & 0xFF;
1345            int len = bytes[i + 1] & 0xFF;
1346
1347            if (i + len + 2 > bytes.length) {
1348                Log.w(TAG, dbg + "bad length " + len + " of IE " + type + " from " + result.BSSID);
1349                Log.w(TAG, dbg + "ignoring the rest of the IEs");
1350                break;
1351            }
1352            num++;
1353            i += len + 2;
1354            if (DBG) Log.i(TAG, dbg + "bytes[" + i + "] = [" + type + ", " + len + "]" + ", " +
1355                    "next = " + i);
1356        }
1357
1358        int secondChanelOffset = 0;
1359        byte channelMode = 0;
1360        byte centerFreqIndex1 = 0;
1361        byte centerFreqIndex2 = 0;
1362        result.is80211McRTTResponder = false;
1363
1364        ScanResult.InformationElement elements[] = new ScanResult.InformationElement[num];
1365        for (int i = 0, index = 0; i < num; i++) {
1366            int type  = bytes[index] & 0xFF;
1367            int len = bytes[index + 1] & 0xFF;
1368            if (DBG) Log.i(TAG, dbg + "index = " + index + ", type = " + type + ", len = " + len);
1369            ScanResult.InformationElement elem = new ScanResult.InformationElement();
1370            elem.id = type;
1371            elem.bytes = new byte[len];
1372            for (int j = 0; j < len; j++) {
1373                elem.bytes[j] = bytes[index + j + 2];
1374            }
1375            elements[i] = elem;
1376            int inforStart = index + 2;
1377            index += (len + 2);
1378
1379            if(type == EID_HT_OPERATION) {
1380                secondChanelOffset = bytes[inforStart + 1] & 0x3;
1381            } else if(type == EID_VHT_OPERATION) {
1382                channelMode = bytes[inforStart];
1383                centerFreqIndex1 = bytes[inforStart + 1];
1384                centerFreqIndex2 = bytes[inforStart + 2];
1385            } else if (type == EID_EXTENDED_CAPS) {
1386                int tempIndex = RTT_RESP_ENABLE_BIT / 8;
1387                byte offset = RTT_RESP_ENABLE_BIT % 8;
1388
1389                if(len < tempIndex + 1) {
1390                    result.is80211McRTTResponder = false;
1391                } else {
1392                    if ((bytes[inforStart + tempIndex] & ((byte)0x1 << offset)) != 0) {
1393                        result.is80211McRTTResponder = true;
1394                    } else {
1395                        result.is80211McRTTResponder = false;
1396                    }
1397                }
1398            }
1399        }
1400        //handle RTT related information
1401        if (channelMode != 0) {
1402            // 80 or 160 MHz
1403            result.channelWidth = channelMode + 1;
1404
1405            //convert channel index to frequency in MHz, channel 36 is 5180MHz
1406            result.centerFreq0 = (centerFreqIndex1 - 36) * 5 + 5180;
1407
1408            if(channelMode > 1) { //160MHz
1409                result.centerFreq1 = (centerFreqIndex2 - 36) * 5 + 5180;
1410            } else {
1411                result.centerFreq1 = 0;
1412            }
1413        } else {
1414            //20 or 40 MHz
1415            if (secondChanelOffset != 0) {//40MHz
1416                result.channelWidth = 1;
1417                if (secondChanelOffset == 1) {
1418                    result.centerFreq0 = result.frequency + 20;
1419                } else if (secondChanelOffset == 3) {
1420                    result.centerFreq0 = result.frequency - 20;
1421                } else {
1422                    result.centerFreq0 = 0;
1423                    Log.e(TAG, dbg + ": Error on secondChanelOffset");
1424                }
1425            } else {
1426                result.centerFreq0  = 0;
1427                result.centerFreq1  = 0;
1428            }
1429            result.centerFreq1  = 0;
1430        }
1431        if(DBG) {
1432            Log.d(TAG, dbg + "SSID: " + result.SSID + " ChannelWidth is: " + result.channelWidth +
1433                    " PrimaryFreq: " + result.frequency +" mCenterfreq0: " + result.centerFreq0 +
1434                    " mCenterfreq1: " + result.centerFreq1 + (result.is80211McRTTResponder ?
1435                    "Support RTT reponder: " : "Do not support RTT responder"));
1436        }
1437
1438        result.informationElements = elements;
1439    }
1440
1441    synchronized static void onFullScanResult(int id, ScanResult result, byte bytes[]) {
1442        if (DBG) Log.i(TAG, "Got a full scan results event, ssid = " + result.SSID + ", " +
1443                "num = " + bytes.length);
1444
1445        if (sScanEventHandler == null) {
1446            return;
1447        }
1448        populateScanResult(result, bytes, " onFullScanResult ");
1449
1450        sScanEventHandler.onFullScanResult(result);
1451    }
1452
1453    private static int sScanCmdId = 0;
1454    private static ScanEventHandler sScanEventHandler;
1455    private static ScanSettings sScanSettings;
1456
1457    synchronized public static boolean startScan(
1458            ScanSettings settings, ScanEventHandler eventHandler) {
1459        synchronized (mLock) {
1460
1461            if (sScanCmdId != 0) {
1462                stopScan();
1463            } else if (sScanSettings != null || sScanEventHandler != null) {
1464                /* current scan is paused; no need to stop it */
1465            }
1466
1467            sScanCmdId = getNewCmdIdLocked();
1468
1469            sScanSettings = settings;
1470            sScanEventHandler = eventHandler;
1471
1472            if (startScanNative(sWlan0Index, sScanCmdId, settings) == false) {
1473                sScanEventHandler = null;
1474                sScanSettings = null;
1475                sScanCmdId = 0;
1476                return false;
1477            }
1478
1479            return true;
1480        }
1481    }
1482
1483    synchronized public static void stopScan() {
1484        synchronized (mLock) {
1485            stopScanNative(sWlan0Index, sScanCmdId);
1486            sScanSettings = null;
1487            sScanEventHandler = null;
1488            sScanCmdId = 0;
1489        }
1490    }
1491
1492    synchronized public static void pauseScan() {
1493        synchronized (mLock) {
1494            if (sScanCmdId != 0 && sScanSettings != null && sScanEventHandler != null) {
1495                Log.d(TAG, "Pausing scan");
1496                WifiScanner.ScanData scanData[] = getScanResultsNative(sWlan0Index, true);
1497                stopScanNative(sWlan0Index, sScanCmdId);
1498                sScanCmdId = 0;
1499                sScanEventHandler.onScanPaused(scanData);
1500            }
1501        }
1502    }
1503
1504    synchronized public static void restartScan() {
1505        synchronized (mLock) {
1506            if (sScanCmdId == 0 && sScanSettings != null && sScanEventHandler != null) {
1507                Log.d(TAG, "Restarting scan");
1508                ScanEventHandler handler = sScanEventHandler;
1509                ScanSettings settings = sScanSettings;
1510                if (startScan(sScanSettings, sScanEventHandler)) {
1511                    sScanEventHandler.onScanRestarted();
1512                } else {
1513                    /* we are still paused; don't change state */
1514                    sScanEventHandler = handler;
1515                    sScanSettings = settings;
1516                }
1517            }
1518        }
1519    }
1520
1521    synchronized public static WifiScanner.ScanData[] getScanResults(boolean flush) {
1522        synchronized (mLock) {
1523            return getScanResultsNative(sWlan0Index, flush);
1524        }
1525    }
1526
1527    public static interface HotlistEventHandler {
1528        void onHotlistApFound (ScanResult[] result);
1529        void onHotlistApLost  (ScanResult[] result);
1530    }
1531
1532    private static int sHotlistCmdId = 0;
1533    private static HotlistEventHandler sHotlistEventHandler;
1534
1535    private native static boolean setHotlistNative(int iface, int id,
1536            WifiScanner.HotlistSettings settings);
1537    private native static boolean resetHotlistNative(int iface, int id);
1538
1539    synchronized public static boolean setHotlist(WifiScanner.HotlistSettings settings,
1540                                    HotlistEventHandler eventHandler) {
1541        synchronized (mLock) {
1542            if (sHotlistCmdId != 0) {
1543                return false;
1544            } else {
1545                sHotlistCmdId = getNewCmdIdLocked();
1546            }
1547
1548            sHotlistEventHandler = eventHandler;
1549            if (setHotlistNative(sWlan0Index, sScanCmdId, settings) == false) {
1550                sHotlistEventHandler = null;
1551                return false;
1552            }
1553
1554            return true;
1555        }
1556    }
1557
1558    synchronized public static void resetHotlist() {
1559        synchronized (mLock) {
1560            if (sHotlistCmdId != 0) {
1561                resetHotlistNative(sWlan0Index, sHotlistCmdId);
1562                sHotlistCmdId = 0;
1563                sHotlistEventHandler = null;
1564            }
1565        }
1566    }
1567
1568    synchronized public static void onHotlistApFound(int id, ScanResult[] results) {
1569        synchronized (mLock) {
1570            if (sHotlistCmdId != 0) {
1571                sHotlistEventHandler.onHotlistApFound(results);
1572            } else {
1573                /* this can happen because of race conditions */
1574                Log.d(TAG, "Ignoring hotlist AP found event");
1575            }
1576        }
1577    }
1578
1579    synchronized public static void onHotlistApLost(int id, ScanResult[] results) {
1580        synchronized (mLock) {
1581            if (sHotlistCmdId != 0) {
1582                sHotlistEventHandler.onHotlistApLost(results);
1583            } else {
1584                /* this can happen because of race conditions */
1585                Log.d(TAG, "Ignoring hotlist AP lost event");
1586            }
1587        }
1588    }
1589
1590    public static interface SignificantWifiChangeEventHandler {
1591        void onChangesFound(ScanResult[] result);
1592    }
1593
1594    private static SignificantWifiChangeEventHandler sSignificantWifiChangeHandler;
1595    private static int sSignificantWifiChangeCmdId;
1596
1597    private static native boolean trackSignificantWifiChangeNative(
1598            int iface, int id, WifiScanner.WifiChangeSettings settings);
1599    private static native boolean untrackSignificantWifiChangeNative(int iface, int id);
1600
1601    synchronized public static boolean trackSignificantWifiChange(
1602            WifiScanner.WifiChangeSettings settings, SignificantWifiChangeEventHandler handler) {
1603        synchronized (mLock) {
1604            if (sSignificantWifiChangeCmdId != 0) {
1605                return false;
1606            } else {
1607                sSignificantWifiChangeCmdId = getNewCmdIdLocked();
1608            }
1609
1610            sSignificantWifiChangeHandler = handler;
1611            if (trackSignificantWifiChangeNative(sWlan0Index, sScanCmdId, settings) == false) {
1612                sSignificantWifiChangeHandler = null;
1613                return false;
1614            }
1615
1616            return true;
1617        }
1618    }
1619
1620    synchronized static void untrackSignificantWifiChange() {
1621        synchronized (mLock) {
1622            if (sSignificantWifiChangeCmdId != 0) {
1623                untrackSignificantWifiChangeNative(sWlan0Index, sSignificantWifiChangeCmdId);
1624                sSignificantWifiChangeCmdId = 0;
1625                sSignificantWifiChangeHandler = null;
1626            }
1627        }
1628    }
1629
1630    synchronized static void onSignificantWifiChange(int id, ScanResult[] results) {
1631        synchronized (mLock) {
1632            if (sSignificantWifiChangeCmdId != 0) {
1633                sSignificantWifiChangeHandler.onChangesFound(results);
1634            } else {
1635                /* this can happen because of race conditions */
1636                Log.d(TAG, "Ignoring significant wifi change");
1637            }
1638        }
1639    }
1640
1641    synchronized public static WifiLinkLayerStats getWifiLinkLayerStats(String iface) {
1642        // TODO: use correct iface name to Index translation
1643        if (iface == null) return null;
1644        synchronized (mLock) {
1645            if (!sHalIsStarted)
1646                startHal();
1647            if (sHalIsStarted)
1648                return getWifiLinkLayerStatsNative(sWlan0Index);
1649        }
1650        return null;
1651    }
1652
1653    /*
1654     * NFC-related calls
1655     */
1656    public String getNfcWpsConfigurationToken(int netId) {
1657        return doStringCommand("WPS_NFC_CONFIG_TOKEN WPS " + netId);
1658    }
1659
1660    public String getNfcHandoverRequest() {
1661        return doStringCommand("NFC_GET_HANDOVER_REQ NDEF P2P-CR");
1662    }
1663
1664    public String getNfcHandoverSelect() {
1665        return doStringCommand("NFC_GET_HANDOVER_SEL NDEF P2P-CR");
1666    }
1667
1668    public boolean initiatorReportNfcHandover(String selectMessage) {
1669        return doBooleanCommand("NFC_REPORT_HANDOVER INIT P2P 00 " + selectMessage);
1670    }
1671
1672    public boolean responderReportNfcHandover(String requestMessage) {
1673        return doBooleanCommand("NFC_REPORT_HANDOVER RESP P2P " + requestMessage + " 00");
1674    }
1675
1676    public static native int getSupportedFeatureSetNative(int iface);
1677    synchronized public static int getSupportedFeatureSet() {
1678        return getSupportedFeatureSetNative(sWlan0Index);
1679    }
1680
1681    /* Rtt related commands/events */
1682    public static interface RttEventHandler {
1683        void onRttResults(RttManager.RttResult[] result);
1684    }
1685
1686    private static RttEventHandler sRttEventHandler;
1687    private static int sRttCmdId;
1688
1689    synchronized private static void onRttResults(int id, RttManager.RttResult[] results) {
1690        if (id == sRttCmdId) {
1691            Log.d(TAG, "Received " + results.length + " rtt results");
1692            sRttEventHandler.onRttResults(results);
1693            sRttCmdId = 0;
1694        } else {
1695            Log.d(TAG, "RTT Received event for unknown cmd = " + id + ", current id = " + sRttCmdId);
1696        }
1697    }
1698
1699    private static native boolean requestRangeNative(
1700            int iface, int id, RttManager.RttParams[] params);
1701    private static native boolean cancelRangeRequestNative(
1702            int iface, int id, RttManager.RttParams[] params);
1703
1704    synchronized public static boolean requestRtt(
1705            RttManager.RttParams[] params, RttEventHandler handler) {
1706        synchronized (mLock) {
1707            if (sRttCmdId != 0) {
1708                Log.v("TAG", "Last one is still under measurement!");
1709                return false;
1710            } else {
1711                sRttCmdId = getNewCmdIdLocked();
1712            }
1713            sRttEventHandler = handler;
1714            Log.v(TAG, "native issue RTT request");
1715            return requestRangeNative(sWlan0Index, sRttCmdId, params);
1716        }
1717    }
1718
1719    synchronized public static boolean cancelRtt(RttManager.RttParams[] params) {
1720        synchronized(mLock) {
1721            if (sRttCmdId == 0) {
1722                return false;
1723            }
1724
1725            sRttCmdId = 0;
1726
1727            if (cancelRangeRequestNative(sWlan0Index, sRttCmdId, params)) {
1728                sRttEventHandler = null;
1729                Log.v(TAG, "Xin: RTT cancel Request Successfully");
1730                return true;
1731            } else {
1732                Log.e(TAG, "Xin:RTT cancel Request failed");
1733                return false;
1734            }
1735        }
1736    }
1737
1738    private static native boolean setScanningMacOuiNative(int iface, byte[] oui);
1739
1740    synchronized public static boolean setScanningMacOui(byte[] oui) {
1741        synchronized (mLock) {
1742            if (startHal()) {
1743                return setScanningMacOuiNative(sWlan0Index, oui);
1744            } else {
1745                return false;
1746            }
1747        }
1748    }
1749
1750    private static native int[] getChannelsForBandNative(
1751            int iface, int band);
1752
1753    synchronized public static int [] getChannelsForBand(int band) {
1754        synchronized (mLock) {
1755            if (startHal()) {
1756                return getChannelsForBandNative(sWlan0Index, band);
1757            } else {
1758                return null;
1759            }
1760        }
1761    }
1762
1763
1764    private static native boolean setDfsFlagNative(int iface, boolean dfsOn);
1765    synchronized public static boolean setDfsFlag(boolean dfsOn) {
1766        synchronized (mLock) {
1767            if (startHal()) {
1768                return setDfsFlagNative(sWlan0Index, dfsOn);
1769            } else {
1770                return false;
1771            }
1772        }
1773    }
1774
1775    private static native boolean toggleInterfaceNative(int on);
1776    synchronized public static boolean toggleInterface(int on) {
1777        synchronized (mLock) {
1778            if (startHal()) {
1779                return toggleInterfaceNative(0);
1780            } else {
1781
1782                return false;
1783            }
1784        }
1785    }
1786
1787    private static native RttManager.RttCapabilities getRttCapabilitiesNative(int iface);
1788    synchronized public static RttManager.RttCapabilities getRttCapabilities() {
1789        synchronized (mLock) {
1790            if (startHal()) {
1791                return getRttCapabilitiesNative(sWlan0Index);
1792            } else {
1793                return null;
1794            }
1795        }
1796    }
1797
1798    private static native boolean setCountryCodeHalNative(int iface, String CountryCode);
1799    synchronized public static boolean setCountryCodeHal( String CountryCode) {
1800        synchronized (mLock) {
1801            if (startHal()) {
1802                return setCountryCodeHalNative(sWlan0Index, CountryCode);
1803            } else {
1804                return false;
1805            }
1806        }
1807    }
1808
1809    /* Rtt related commands/events */
1810    public abstract class TdlsEventHandler {
1811        abstract public void onTdlsStatus(String macAddr, int status, int reason);
1812    }
1813
1814    private static TdlsEventHandler sTdlsEventHandler;
1815
1816
1817    private static native boolean enableDisableTdlsNative(int iface, boolean enable,
1818            String macAddr);
1819    synchronized public static boolean enableDisableTdls(boolean enable, String macAdd,
1820            TdlsEventHandler tdlsCallBack) {
1821        synchronized (mLock) {
1822            if (startHal()) {
1823                sTdlsEventHandler = tdlsCallBack;
1824                return enableDisableTdlsNative(sWlan0Index, enable, macAdd);
1825            } else {
1826                return false;
1827            }
1828        }
1829    }
1830
1831    // Once TDLS per mac and event feature is implemented, this class definition should be
1832    // moved to the right place, like WifiManager etc
1833    public static class TdlsStatus {
1834        int channel;
1835        int global_operating_class;
1836        int state;
1837        int reason;
1838    }
1839    private static native TdlsStatus getTdlsStatusNative(int iface, String macAddr);
1840    synchronized public static TdlsStatus getTdlsStatus (String macAdd) {
1841        synchronized (mLock) {
1842            if (startHal()) {
1843                return getTdlsStatusNative(sWlan0Index, macAdd);
1844            } else {
1845                return null;
1846            }
1847        }
1848    }
1849
1850    //ToFix: Once TDLS per mac and event feature is implemented, this class definition should be
1851    // moved to the right place, like WifiStateMachine etc
1852    public static class TdlsCapabilities {
1853        /* Maximum TDLS session number can be supported by the Firmware and hardware */
1854        int maxConcurrentTdlsSessionNumber;
1855        boolean isGlobalTdlsSupported;
1856        boolean isPerMacTdlsSupported;
1857        boolean isOffChannelTdlsSupported;
1858    }
1859
1860
1861
1862    private static native TdlsCapabilities getTdlsCapabilitiesNative(int iface);
1863    synchronized public static TdlsCapabilities getTdlsCapabilities () {
1864        synchronized (mLock) {
1865            if (startHal()) {
1866                return getTdlsCapabilitiesNative(sWlan0Index);
1867            } else {
1868                return null;
1869            }
1870        }
1871    }
1872
1873    synchronized private static boolean onTdlsStatus(String macAddr, int status, int reason) {
1874         if (sTdlsEventHandler == null) {
1875             return false;
1876         } else {
1877             sTdlsEventHandler.onTdlsStatus(macAddr, status, reason);
1878             return true;
1879         }
1880    }
1881
1882    //---------------------------------------------------------------------------------
1883
1884    /* Wifi Logger commands/events */
1885
1886    private static native boolean startLogging(int iface);
1887
1888    public static interface WifiLoggerEventHandler {
1889        void onDataAvailable(char data[], int len);
1890    }
1891
1892    private static WifiLoggerEventHandler sWifiLoggerEventHandler = null;
1893
1894    synchronized private static void onDataAvailable(char data[], int len) {
1895        if (sWifiLoggerEventHandler != null) {
1896            sWifiLoggerEventHandler.onDataAvailable(data, len);
1897        }
1898    }
1899
1900    //---------------------------------------------------------------------------------
1901    /* Configure ePNO */
1902
1903    public class WifiPnoNetwork {
1904        String SSID;
1905        int rssi_threshold;
1906        int flags;
1907        int auth;
1908        String configKey; // kept for reference
1909
1910        WifiPnoNetwork(WifiConfiguration config, int threshold) {
1911            if (config.SSID == null) {
1912                this.SSID = "";
1913                this.flags = 1;
1914            } else {
1915                this.SSID = config.SSID;
1916            }
1917            this.rssi_threshold = threshold;
1918            if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
1919                auth |= 2;
1920            } else if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP) ||
1921                    config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X)) {
1922                auth |= 4;
1923            } else if (config.wepKeys[0] != null) {
1924                auth |= 1;
1925            } else {
1926                auth |= 1;
1927            }
1928//            auth = 0;
1929            flags |= 6; //A and G
1930            configKey = config.configKey();
1931        }
1932
1933        @Override
1934        public String toString() {
1935            StringBuilder sbuf = new StringBuilder();
1936            sbuf.append(this.SSID);
1937            sbuf.append(" flags=").append(this.flags);
1938            sbuf.append(" rssi=").append(this.rssi_threshold);
1939            sbuf.append(" auth=").append(this.auth);
1940            return sbuf.toString();
1941        }
1942    }
1943
1944    public static interface WifiPnoEventHandler {
1945        void onPnoNetworkFound(ScanResult results[]);
1946    }
1947
1948    private static WifiPnoEventHandler sWifiPnoEventHandler;
1949
1950    private static int sPnoCmdId = 0;
1951
1952    private native static boolean setPnoListNative(int iface, int id, WifiPnoNetwork list[]);
1953
1954    synchronized public static boolean setPnoList(WifiPnoNetwork list[],
1955                                                  WifiPnoEventHandler eventHandler) {
1956        Log.e(TAG, "setPnoList cmd " + sPnoCmdId);
1957
1958        synchronized (mLock) {
1959
1960            sPnoCmdId = getNewCmdIdLocked();
1961
1962            sWifiPnoEventHandler = eventHandler;
1963            if (setPnoListNative(sWlan0Index, sPnoCmdId, list) == false) {
1964                sWifiPnoEventHandler = null;
1965                return false;
1966            }
1967
1968            return true;
1969        }
1970    }
1971
1972    synchronized public static void onPnoNetworkFound(int id, ScanResult[] results) {
1973
1974        if (results == null) {
1975            Log.e(TAG, "onPnoNetworkFound null results");
1976            return;
1977
1978        }
1979        Log.d(TAG, "WifiNative.onPnoNetworkFound result " + results.length);
1980
1981        //Log.e(TAG, "onPnoNetworkFound length " + results.length);
1982        //return;
1983        for (int i=0; i<results.length; i++) {
1984            Log.e(TAG, "onPnoNetworkFound SSID " + results[i].SSID
1985                    + " " + results[i].level + " " + results[i].frequency);
1986
1987            populateScanResult(results[i], results[i].bytes, "onPnoNetworkFound ");
1988            results[i].wifiSsid = WifiSsid.createFromAsciiEncoded(results[i].SSID);
1989        }
1990        synchronized (mLock) {
1991            if (sPnoCmdId != 0 && sWifiPnoEventHandler != null) {
1992                sWifiPnoEventHandler.onPnoNetworkFound(results);
1993            } else {
1994                /* this can happen because of race conditions */
1995                Log.d(TAG, "Ignoring Pno Network found event");
1996            }
1997        }
1998}
1999
2000}
2001