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