WifiNative.java revision 51dad1d02cb0c3308ea26df198f3057c69690add
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                new MonitorThread().start();
1216                sHalIsStarted = true;
1217                return true;
1218            } else {
1219                Log.i(TAG, "Could not start hal");
1220                sHalIsStarted = false;
1221                sHalFailed = true;
1222                return false;
1223            }
1224        }
1225    }
1226
1227    synchronized public static void stopHal() {
1228        stopHalNative();
1229    }
1230
1231    private static native int getInterfacesNative();
1232
1233    synchronized public static int getInterfaces() {
1234        synchronized (mLock) {
1235            if (sWifiIfaceHandles == null) {
1236                int num = getInterfacesNative();
1237                int wifi_num = 0;
1238                for (int i = 0; i < num; i++) {
1239                    String name = getInterfaceNameNative(i);
1240                    Log.i(TAG, "interface[" + i + "] = " + name);
1241                    if (name.equals("wlan0")) {
1242                        sWlan0Index = i;
1243                        wifi_num++;
1244                    } else if (name.equals("p2p0")) {
1245                        sP2p0Index = i;
1246                        wifi_num++;
1247                    }
1248                }
1249                return wifi_num;
1250            } else {
1251                return sWifiIfaceHandles.length;
1252            }
1253        }
1254    }
1255
1256    private static native String getInterfaceNameNative(int index);
1257    synchronized public static String getInterfaceName(int index) {
1258        return getInterfaceNameNative(index);
1259    }
1260
1261    public static class ScanCapabilities {
1262        public int  max_scan_cache_size;                 // in number of scan results??
1263        public int  max_scan_buckets;
1264        public int  max_ap_cache_per_scan;
1265        public int  max_rssi_sample_size;
1266        public int  max_scan_reporting_threshold;        // in number of scan results??
1267        public int  max_hotlist_bssids;
1268        public int  max_significant_wifi_change_aps;
1269    }
1270
1271    public static boolean getScanCapabilities(ScanCapabilities capabilities) {
1272        return getScanCapabilitiesNative(sWlan0Index, capabilities);
1273    }
1274
1275    private static native boolean getScanCapabilitiesNative(
1276            int iface, ScanCapabilities capabilities);
1277
1278    private static native boolean startScanNative(int iface, int id, ScanSettings settings);
1279    private static native boolean stopScanNative(int iface, int id);
1280    private static native WifiScanner.ScanData[] getScanResultsNative(int iface, boolean flush);
1281    private static native WifiLinkLayerStats getWifiLinkLayerStatsNative(int iface);
1282
1283    public static class ChannelSettings {
1284        int frequency;
1285        int dwell_time_ms;
1286        boolean passive;
1287    }
1288
1289    public static class BucketSettings {
1290        int bucket;
1291        int band;
1292        int period_ms;
1293        int report_events;
1294        int num_channels;
1295        ChannelSettings channels[];
1296    }
1297
1298    public static class ScanSettings {
1299        int base_period_ms;
1300        int max_ap_per_scan;
1301        int report_threshold_percent;
1302        int report_threshold_num_scans;
1303        int num_buckets;
1304        BucketSettings buckets[];
1305    }
1306
1307    public static interface ScanEventHandler {
1308        void onScanResultsAvailable();
1309        void onFullScanResult(ScanResult fullScanResult);
1310        void onScanStatus();
1311        void onScanPaused(WifiScanner.ScanData[] data);
1312        void onScanRestarted();
1313    }
1314
1315    synchronized static void onScanResultsAvailable(int id) {
1316        if (sScanEventHandler  != null) {
1317            sScanEventHandler.onScanResultsAvailable();
1318        }
1319    }
1320
1321    /* scan status, keep these values in sync with gscan.h */
1322    private static int WIFI_SCAN_BUFFER_FULL = 0;
1323    private static int WIFI_SCAN_COMPLETE = 1;
1324
1325    synchronized static void onScanStatus(int status) {
1326        Log.i(TAG, "Got a scan status changed event, status = " + status);
1327
1328        if (status == WIFI_SCAN_BUFFER_FULL) {
1329            /* we have a separate event to take care of this */
1330        } else if (status == WIFI_SCAN_COMPLETE) {
1331            if (sScanEventHandler  != null) {
1332                sScanEventHandler.onScanStatus();
1333            }
1334        }
1335    }
1336
1337    static void populateScanResult(ScanResult result, byte bytes[], String dbg) {
1338        int num = 0;
1339        if (bytes == null) return;
1340        if (dbg == null) dbg = "";
1341        for (int i = 0; i < bytes.length; ) {
1342            int type  = bytes[i] & 0xFF;
1343            int len = bytes[i + 1] & 0xFF;
1344
1345            if (i + len + 2 > bytes.length) {
1346                Log.w(TAG, dbg + "bad length " + len + " of IE " + type + " from " + result.BSSID);
1347                Log.w(TAG, dbg + "ignoring the rest of the IEs");
1348                break;
1349            }
1350            num++;
1351            i += len + 2;
1352            if (DBG) Log.i(TAG, dbg + "bytes[" + i + "] = [" + type + ", " + len + "]" + ", " +
1353                    "next = " + i);
1354        }
1355
1356        int secondChanelOffset = 0;
1357        byte channelMode = 0;
1358        byte centerFreqIndex1 = 0;
1359        byte centerFreqIndex2 = 0;
1360        result.is80211McRTTResponder = false;
1361
1362        ScanResult.InformationElement elements[] = new ScanResult.InformationElement[num];
1363        for (int i = 0, index = 0; i < num; i++) {
1364            int type  = bytes[index] & 0xFF;
1365            int len = bytes[index + 1] & 0xFF;
1366            if (DBG) Log.i(TAG, dbg + "index = " + index + ", type = " + type + ", len = " + len);
1367            ScanResult.InformationElement elem = new ScanResult.InformationElement();
1368            elem.id = type;
1369            elem.bytes = new byte[len];
1370            for (int j = 0; j < len; j++) {
1371                elem.bytes[j] = bytes[index + j + 2];
1372            }
1373            elements[i] = elem;
1374            int inforStart = index + 2;
1375            index += (len + 2);
1376
1377            if(type == EID_HT_OPERATION) {
1378                secondChanelOffset = bytes[inforStart + 1] & 0x3;
1379            } else if(type == EID_VHT_OPERATION) {
1380                channelMode = bytes[inforStart];
1381                centerFreqIndex1 = bytes[inforStart + 1];
1382                centerFreqIndex2 = bytes[inforStart + 2];
1383            } else if (type == EID_EXTENDED_CAPS) {
1384                int tempIndex = RTT_RESP_ENABLE_BIT / 8;
1385                byte offset = RTT_RESP_ENABLE_BIT % 8;
1386
1387                if(len < tempIndex + 1) {
1388                    result.is80211McRTTResponder = false;
1389                } else {
1390                    if ((bytes[inforStart + tempIndex] & ((byte)0x1 << offset)) != 0) {
1391                        result.is80211McRTTResponder = true;
1392                    } else {
1393                        result.is80211McRTTResponder = false;
1394                    }
1395                }
1396            }
1397        }
1398        //handle RTT related information
1399        if (channelMode != 0) {
1400            // 80 or 160 MHz
1401            result.channelWidth = channelMode + 1;
1402
1403            //convert channel index to frequency in MHz, channel 36 is 5180MHz
1404            result.centerFreq0 = (centerFreqIndex1 - 36) * 5 + 5180;
1405
1406            if(channelMode > 1) { //160MHz
1407                result.centerFreq1 = (centerFreqIndex2 - 36) * 5 + 5180;
1408            } else {
1409                result.centerFreq1 = 0;
1410            }
1411        } else {
1412            //20 or 40 MHz
1413            if (secondChanelOffset != 0) {//40MHz
1414                result.channelWidth = 1;
1415                if (secondChanelOffset == 1) {
1416                    result.centerFreq0 = result.frequency + 20;
1417                } else if (secondChanelOffset == 3) {
1418                    result.centerFreq0 = result.frequency - 20;
1419                } else {
1420                    result.centerFreq0 = 0;
1421                    Log.e(TAG, dbg + ": Error on secondChanelOffset");
1422                }
1423            } else {
1424                result.centerFreq0  = 0;
1425                result.centerFreq1  = 0;
1426            }
1427            result.centerFreq1  = 0;
1428        }
1429        if(DBG) {
1430            Log.d(TAG, dbg + "SSID: " + result.SSID + " ChannelWidth is: " + result.channelWidth +
1431                    " PrimaryFreq: " + result.frequency +" mCenterfreq0: " + result.centerFreq0 +
1432                    " mCenterfreq1: " + result.centerFreq1 + (result.is80211McRTTResponder ?
1433                    "Support RTT reponder: " : "Do not support RTT responder"));
1434        }
1435
1436        result.informationElements = elements;
1437    }
1438
1439    synchronized static void onFullScanResult(int id, ScanResult result, byte bytes[]) {
1440        if (DBG) Log.i(TAG, "Got a full scan results event, ssid = " + result.SSID + ", " +
1441                "num = " + bytes.length);
1442
1443        if (sScanEventHandler == null) {
1444            return;
1445        }
1446        populateScanResult(result, bytes, " onFullScanResult ");
1447
1448        sScanEventHandler.onFullScanResult(result);
1449    }
1450
1451    private static int sScanCmdId = 0;
1452    private static ScanEventHandler sScanEventHandler;
1453    private static ScanSettings sScanSettings;
1454
1455    synchronized public static boolean startScan(
1456            ScanSettings settings, ScanEventHandler eventHandler) {
1457        synchronized (mLock) {
1458
1459            if (sScanCmdId != 0) {
1460                stopScan();
1461            } else if (sScanSettings != null || sScanEventHandler != null) {
1462                /* current scan is paused; no need to stop it */
1463            }
1464
1465            sScanCmdId = getNewCmdIdLocked();
1466
1467            sScanSettings = settings;
1468            sScanEventHandler = eventHandler;
1469
1470            if (startScanNative(sWlan0Index, sScanCmdId, settings) == false) {
1471                sScanEventHandler = null;
1472                sScanSettings = null;
1473                sScanCmdId = 0;
1474                return false;
1475            }
1476
1477            return true;
1478        }
1479    }
1480
1481    synchronized public static void stopScan() {
1482        synchronized (mLock) {
1483            stopScanNative(sWlan0Index, sScanCmdId);
1484            sScanSettings = null;
1485            sScanEventHandler = null;
1486            sScanCmdId = 0;
1487        }
1488    }
1489
1490    synchronized public static void pauseScan() {
1491        synchronized (mLock) {
1492            if (sScanCmdId != 0 && sScanSettings != null && sScanEventHandler != null) {
1493                Log.d(TAG, "Pausing scan");
1494                WifiScanner.ScanData scanData[] = getScanResultsNative(sWlan0Index, true);
1495                stopScanNative(sWlan0Index, sScanCmdId);
1496                sScanCmdId = 0;
1497                sScanEventHandler.onScanPaused(scanData);
1498            }
1499        }
1500    }
1501
1502    synchronized public static void restartScan() {
1503        synchronized (mLock) {
1504            if (sScanCmdId == 0 && sScanSettings != null && sScanEventHandler != null) {
1505                Log.d(TAG, "Restarting scan");
1506                ScanEventHandler handler = sScanEventHandler;
1507                ScanSettings settings = sScanSettings;
1508                if (startScan(sScanSettings, sScanEventHandler)) {
1509                    sScanEventHandler.onScanRestarted();
1510                } else {
1511                    /* we are still paused; don't change state */
1512                    sScanEventHandler = handler;
1513                    sScanSettings = settings;
1514                }
1515            }
1516        }
1517    }
1518
1519    synchronized public static WifiScanner.ScanData[] getScanResults(boolean flush) {
1520        synchronized (mLock) {
1521            return getScanResultsNative(sWlan0Index, flush);
1522        }
1523    }
1524
1525    public static interface HotlistEventHandler {
1526        void onHotlistApFound (ScanResult[] result);
1527        void onHotlistApLost  (ScanResult[] result);
1528    }
1529
1530    private static int sHotlistCmdId = 0;
1531    private static HotlistEventHandler sHotlistEventHandler;
1532
1533    private native static boolean setHotlistNative(int iface, int id,
1534            WifiScanner.HotlistSettings settings);
1535    private native static boolean resetHotlistNative(int iface, int id);
1536
1537    synchronized public static boolean setHotlist(WifiScanner.HotlistSettings settings,
1538                                    HotlistEventHandler eventHandler) {
1539        synchronized (mLock) {
1540            if (sHotlistCmdId != 0) {
1541                return false;
1542            } else {
1543                sHotlistCmdId = getNewCmdIdLocked();
1544            }
1545
1546            sHotlistEventHandler = eventHandler;
1547            if (setHotlistNative(sWlan0Index, sScanCmdId, settings) == false) {
1548                sHotlistEventHandler = null;
1549                return false;
1550            }
1551
1552            return true;
1553        }
1554    }
1555
1556    synchronized public static void resetHotlist() {
1557        synchronized (mLock) {
1558            if (sHotlistCmdId != 0) {
1559                resetHotlistNative(sWlan0Index, sHotlistCmdId);
1560                sHotlistCmdId = 0;
1561                sHotlistEventHandler = null;
1562            }
1563        }
1564    }
1565
1566    synchronized public static void onHotlistApFound(int id, ScanResult[] results) {
1567        synchronized (mLock) {
1568            if (sHotlistCmdId != 0) {
1569                sHotlistEventHandler.onHotlistApFound(results);
1570            } else {
1571                /* this can happen because of race conditions */
1572                Log.d(TAG, "Ignoring hotlist AP found event");
1573            }
1574        }
1575    }
1576
1577    synchronized public static void onHotlistApLost(int id, ScanResult[] results) {
1578        synchronized (mLock) {
1579            if (sHotlistCmdId != 0) {
1580                sHotlistEventHandler.onHotlistApLost(results);
1581            } else {
1582                /* this can happen because of race conditions */
1583                Log.d(TAG, "Ignoring hotlist AP lost event");
1584            }
1585        }
1586    }
1587
1588    public static interface SignificantWifiChangeEventHandler {
1589        void onChangesFound(ScanResult[] result);
1590    }
1591
1592    private static SignificantWifiChangeEventHandler sSignificantWifiChangeHandler;
1593    private static int sSignificantWifiChangeCmdId;
1594
1595    private static native boolean trackSignificantWifiChangeNative(
1596            int iface, int id, WifiScanner.WifiChangeSettings settings);
1597    private static native boolean untrackSignificantWifiChangeNative(int iface, int id);
1598
1599    synchronized public static boolean trackSignificantWifiChange(
1600            WifiScanner.WifiChangeSettings settings, SignificantWifiChangeEventHandler handler) {
1601        synchronized (mLock) {
1602            if (sSignificantWifiChangeCmdId != 0) {
1603                return false;
1604            } else {
1605                sSignificantWifiChangeCmdId = getNewCmdIdLocked();
1606            }
1607
1608            sSignificantWifiChangeHandler = handler;
1609            if (trackSignificantWifiChangeNative(sWlan0Index, sScanCmdId, settings) == false) {
1610                sSignificantWifiChangeHandler = null;
1611                return false;
1612            }
1613
1614            return true;
1615        }
1616    }
1617
1618    synchronized static void untrackSignificantWifiChange() {
1619        synchronized (mLock) {
1620            if (sSignificantWifiChangeCmdId != 0) {
1621                untrackSignificantWifiChangeNative(sWlan0Index, sSignificantWifiChangeCmdId);
1622                sSignificantWifiChangeCmdId = 0;
1623                sSignificantWifiChangeHandler = null;
1624            }
1625        }
1626    }
1627
1628    synchronized static void onSignificantWifiChange(int id, ScanResult[] results) {
1629        synchronized (mLock) {
1630            if (sSignificantWifiChangeCmdId != 0) {
1631                sSignificantWifiChangeHandler.onChangesFound(results);
1632            } else {
1633                /* this can happen because of race conditions */
1634                Log.d(TAG, "Ignoring significant wifi change");
1635            }
1636        }
1637    }
1638
1639    synchronized public static WifiLinkLayerStats getWifiLinkLayerStats(String iface) {
1640        // TODO: use correct iface name to Index translation
1641        if (iface == null) return null;
1642        synchronized (mLock) {
1643            if (!sHalIsStarted)
1644                startHal();
1645            if (sHalIsStarted)
1646                return getWifiLinkLayerStatsNative(sWlan0Index);
1647        }
1648        return null;
1649    }
1650
1651    /*
1652     * NFC-related calls
1653     */
1654    public String getNfcWpsConfigurationToken(int netId) {
1655        return doStringCommand("WPS_NFC_CONFIG_TOKEN WPS " + netId);
1656    }
1657
1658    public String getNfcHandoverRequest() {
1659        return doStringCommand("NFC_GET_HANDOVER_REQ NDEF P2P-CR");
1660    }
1661
1662    public String getNfcHandoverSelect() {
1663        return doStringCommand("NFC_GET_HANDOVER_SEL NDEF P2P-CR");
1664    }
1665
1666    public boolean initiatorReportNfcHandover(String selectMessage) {
1667        return doBooleanCommand("NFC_REPORT_HANDOVER INIT P2P 00 " + selectMessage);
1668    }
1669
1670    public boolean responderReportNfcHandover(String requestMessage) {
1671        return doBooleanCommand("NFC_REPORT_HANDOVER RESP P2P " + requestMessage + " 00");
1672    }
1673
1674    public static native int getSupportedFeatureSetNative(int iface);
1675    synchronized public static int getSupportedFeatureSet() {
1676        return getSupportedFeatureSetNative(sWlan0Index);
1677    }
1678
1679    /* Rtt related commands/events */
1680    public static interface RttEventHandler {
1681        void onRttResults(RttManager.RttResult[] result);
1682    }
1683
1684    private static RttEventHandler sRttEventHandler;
1685    private static int sRttCmdId;
1686
1687    synchronized private static void onRttResults(int id, RttManager.RttResult[] results) {
1688        if (id == sRttCmdId) {
1689            Log.d(TAG, "Received " + results.length + " rtt results");
1690            sRttEventHandler.onRttResults(results);
1691            sRttCmdId = 0;
1692        } else {
1693            Log.d(TAG, "Received event for unknown cmd = " + id + ", current id = " + sRttCmdId);
1694        }
1695    }
1696
1697    private static native boolean requestRangeNative(
1698            int iface, int id, RttManager.RttParams[] params);
1699    private static native boolean cancelRangeRequestNative(
1700            int iface, int id, RttManager.RttParams[] params);
1701
1702    synchronized public static boolean requestRtt(
1703            RttManager.RttParams[] params, RttEventHandler handler) {
1704        synchronized (mLock) {
1705            if (sRttCmdId != 0) {
1706                return false;
1707            } else {
1708                sRttCmdId = getNewCmdIdLocked();
1709            }
1710            sRttEventHandler = handler;
1711            return requestRangeNative(sWlan0Index, sRttCmdId, params);
1712        }
1713    }
1714
1715    synchronized public static boolean cancelRtt(RttManager.RttParams[] params) {
1716        synchronized(mLock) {
1717            if (sRttCmdId == 0) {
1718                return false;
1719            }
1720
1721            if (cancelRangeRequestNative(sWlan0Index, sRttCmdId, params)) {
1722                sRttEventHandler = null;
1723                return true;
1724            } else {
1725                return false;
1726            }
1727        }
1728    }
1729
1730    private static native boolean setScanningMacOuiNative(int iface, byte[] oui);
1731
1732    synchronized public static boolean setScanningMacOui(byte[] oui) {
1733        synchronized (mLock) {
1734            if (startHal()) {
1735                return setScanningMacOuiNative(sWlan0Index, oui);
1736            } else {
1737                return false;
1738            }
1739        }
1740    }
1741
1742    private static native int[] getChannelsForBandNative(
1743            int iface, int band);
1744
1745    synchronized public static int [] getChannelsForBand(int band) {
1746        synchronized (mLock) {
1747            if (startHal()) {
1748                return getChannelsForBandNative(sWlan0Index, band);
1749            } else {
1750                return null;
1751            }
1752        }
1753    }
1754
1755
1756    private static native boolean setDfsFlagNative(int iface, boolean dfsOn);
1757    synchronized public static boolean setDfsFlag(boolean dfsOn) {
1758        synchronized (mLock) {
1759            if (startHal()) {
1760                return setDfsFlagNative(sWlan0Index, dfsOn);
1761            } else {
1762                return false;
1763            }
1764        }
1765    }
1766
1767    private static native boolean toggleInterfaceNative(int on);
1768    synchronized public static boolean toggleInterface(int on) {
1769        synchronized (mLock) {
1770            if (startHal()) {
1771                return toggleInterfaceNative(0);
1772            } else {
1773
1774                return false;
1775            }
1776        }
1777    }
1778
1779    private static native RttManager.RttCapabilities getRttCapabilitiesNative(int iface);
1780    synchronized public static RttManager.RttCapabilities getRttCapabilities() {
1781        synchronized (mLock) {
1782            if (startHal()) {
1783                return getRttCapabilitiesNative(sWlan0Index);
1784            } else {
1785                return null;
1786            }
1787        }
1788    }
1789
1790    private static native boolean setCountryCodeHalNative(int iface, String CountryCode);
1791    synchronized public static boolean setCountryCodeHal( String CountryCode) {
1792        synchronized (mLock) {
1793            if (startHal()) {
1794                return setCountryCodeHalNative(sWlan0Index, CountryCode);
1795            } else {
1796                return false;
1797            }
1798        }
1799    }
1800
1801    //---------------------------------------------------------------------------------
1802
1803    /* Wifi Logger commands/events */
1804
1805    private static native boolean startLogging(int iface);
1806
1807    public static interface WifiLoggerEventHandler {
1808        void onDataAvailable(char data[], int len);
1809    }
1810
1811    private static WifiLoggerEventHandler sWifiLoggerEventHandler = null;
1812
1813    synchronized private static void onDataAvailable(char data[], int len) {
1814        if (sWifiLoggerEventHandler != null) {
1815            sWifiLoggerEventHandler.onDataAvailable(data, len);
1816        }
1817    }
1818
1819    //---------------------------------------------------------------------------------
1820    /* Configure ePNO */
1821
1822    public class WifiPnoNetwork {
1823        String SSID;
1824        int rssi_threshold;
1825        int flags;
1826        int auth;
1827        String configKey; // kept for reference
1828
1829        WifiPnoNetwork(WifiConfiguration config, int threshold) {
1830            if (config.SSID == null) {
1831                this.SSID = "";
1832                this.flags = 1;
1833            } else {
1834                this.SSID = config.SSID;
1835            }
1836            this.rssi_threshold = threshold;
1837            if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_PSK)) {
1838                auth |= 2;
1839            } else if (config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.WPA_EAP) ||
1840                    config.allowedKeyManagement.get(WifiConfiguration.KeyMgmt.IEEE8021X)) {
1841                auth |= 4;
1842            } else if (config.wepKeys[0] != null) {
1843                auth |= 1;
1844            } else {
1845                auth |= 1;
1846            }
1847//            auth = 0;
1848            flags |= 6; //A and G
1849            configKey = config.configKey();
1850        }
1851
1852        @Override
1853        public String toString() {
1854            StringBuilder sbuf = new StringBuilder();
1855            sbuf.append(this.SSID);
1856            sbuf.append(" flags=").append(this.flags);
1857            sbuf.append(" rssi=").append(this.rssi_threshold);
1858            sbuf.append(" auth=").append(this.auth);
1859            return sbuf.toString();
1860        }
1861    }
1862
1863    public static interface WifiPnoEventHandler {
1864        void onPnoNetworkFound(ScanResult results[]);
1865    }
1866
1867    private static WifiPnoEventHandler sWifiPnoEventHandler;
1868
1869    private static int sPnoCmdId = 0;
1870
1871    private native static boolean setPnoListNative(int iface, int id, WifiPnoNetwork list[]);
1872
1873    synchronized public static boolean setPnoList(WifiPnoNetwork list[],
1874                                                  WifiPnoEventHandler eventHandler) {
1875        Log.e(TAG, "setPnoList cmd " + sPnoCmdId);
1876
1877        synchronized (mLock) {
1878
1879            sPnoCmdId = getNewCmdIdLocked();
1880
1881            sWifiPnoEventHandler = eventHandler;
1882            if (setPnoListNative(sWlan0Index, sPnoCmdId, list) == false) {
1883                sWifiPnoEventHandler = null;
1884                return false;
1885            }
1886
1887            return true;
1888        }
1889    }
1890
1891    synchronized public static void onPnoNetworkFound(int id, ScanResult[] results) {
1892
1893        if (results == null) {
1894            Log.e(TAG, "onPnoNetworkFound null results");
1895            return;
1896
1897        }
1898        Log.d(TAG, "WifiNative.onPnoNetworkFound result " + results.length);
1899
1900        //Log.e(TAG, "onPnoNetworkFound length " + results.length);
1901        //return;
1902        for (int i=0; i<results.length; i++) {
1903            Log.e(TAG, "onPnoNetworkFound SSID " + results[i].SSID
1904                    + " " + results[i].level + " " + results[i].frequency);
1905
1906            populateScanResult(results[i], results[i].bytes, "onPnoNetworkFound ");
1907            results[i].wifiSsid = WifiSsid.createFromAsciiEncoded(results[i].SSID);
1908        }
1909        synchronized (mLock) {
1910            if (sPnoCmdId != 0 && sWifiPnoEventHandler != null) {
1911                sWifiPnoEventHandler.onPnoNetworkFound(results);
1912            } else {
1913                /* this can happen because of race conditions */
1914                Log.d(TAG, "Ignoring Pno Network found event");
1915            }
1916        }
1917}
1918
1919}
1920