WifiNative.java revision 64697f7a5b9c1c39f8c9d9c225b8ca4c798422c2
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.annotation.Nullable;
20import android.app.AlarmManager;
21import android.app.PendingIntent;
22import android.content.BroadcastReceiver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.net.apf.ApfCapabilities;
27import android.net.wifi.RttManager;
28import android.net.wifi.RttManager.ResponderConfig;
29import android.net.wifi.ScanResult;
30import android.net.wifi.WifiConfiguration;
31import android.net.wifi.WifiEnterpriseConfig;
32import android.net.wifi.WifiLinkLayerStats;
33import android.net.wifi.WifiManager;
34import android.net.wifi.WifiScanner;
35import android.net.wifi.WifiSsid;
36import android.net.wifi.WifiWakeReasonAndCounts;
37import android.net.wifi.WpsInfo;
38import android.net.wifi.p2p.WifiP2pConfig;
39import android.net.wifi.p2p.WifiP2pGroup;
40import android.net.wifi.p2p.nsd.WifiP2pServiceInfo;
41import android.os.SystemClock;
42import android.os.SystemProperties;
43import android.text.TextUtils;
44import android.util.LocalLog;
45import android.util.Log;
46
47import com.android.server.connectivity.KeepalivePacketData;
48import com.android.server.wifi.hotspot2.NetworkDetail;
49import com.android.server.wifi.hotspot2.SupplicantBridge;
50import com.android.server.wifi.hotspot2.Utils;
51import com.android.server.wifi.util.InformationElementUtil;
52
53import libcore.util.HexEncoding;
54
55import org.json.JSONException;
56import org.json.JSONObject;
57
58import java.io.UnsupportedEncodingException;
59import java.net.URLDecoder;
60import java.net.URLEncoder;
61import java.nio.ByteBuffer;
62import java.nio.CharBuffer;
63import java.nio.charset.CharacterCodingException;
64import java.nio.charset.CharsetDecoder;
65import java.nio.charset.StandardCharsets;
66import java.util.ArrayList;
67import java.util.BitSet;
68import java.util.HashMap;
69import java.util.Iterator;
70import java.util.List;
71import java.util.Locale;
72import java.util.Map;
73import java.util.Set;
74
75
76/**
77 * Native calls for bring up/shut down of the supplicant daemon and for
78 * sending requests to the supplicant daemon
79 *
80 * waitForEvent() is called on the monitor thread for events. All other methods
81 * must be serialized from the framework.
82 *
83 * {@hide}
84 */
85public class WifiNative {
86    private static boolean DBG = false;
87
88    /**
89     * Hold this lock before calling supplicant or HAL methods
90     * it is required to mutually exclude access to the driver
91     */
92    public static final Object sLock = new Object();
93
94    private static final LocalLog sLocalLog = new LocalLog(16384);
95
96    public static LocalLog getLocalLog() {
97        return sLocalLog;
98    }
99
100    /* Register native functions */
101    static {
102        /* Native functions are defined in libwifi-service.so */
103        System.loadLibrary("wifi-service");
104        registerNatives();
105    }
106
107    private static native int registerNatives();
108
109    /*
110     * Singleton WifiNative instances
111     */
112    private static WifiNative wlanNativeInterface =
113            new WifiNative(SystemProperties.get("wifi.interface", "wlan0"), true);
114    public static WifiNative getWlanNativeInterface() {
115        return wlanNativeInterface;
116    }
117
118    private static WifiNative p2pNativeInterface =
119            // commands for p2p0 interface don't need prefix
120            new WifiNative(SystemProperties.get("wifi.direct.interface", "p2p0"), false);
121    public static WifiNative getP2pNativeInterface() {
122        return p2pNativeInterface;
123    }
124
125
126    private final String mTAG;
127    private final String mInterfaceName;
128    private final String mInterfacePrefix;
129
130    private Context mContext = null;
131    private PnoMonitor mPnoMonitor = null;
132    public void initContext(Context context) {
133        if (mContext == null && context != null) {
134            mContext = context;
135            mPnoMonitor = new PnoMonitor();
136        }
137    }
138
139    private WifiNative(String interfaceName,
140                       boolean requiresPrefix) {
141        mInterfaceName = interfaceName;
142        mTAG = "WifiNative-" + interfaceName;
143
144        if (requiresPrefix) {
145            mInterfacePrefix = "IFNAME=" + interfaceName + " ";
146        } else {
147            mInterfacePrefix = "";
148        }
149    }
150
151    public String getInterfaceName() {
152        return mInterfaceName;
153    }
154
155    // Note this affects logging on for all interfaces
156    void enableVerboseLogging(int verbose) {
157        if (verbose > 0) {
158            DBG = true;
159        } else {
160            DBG = false;
161        }
162    }
163
164    private void localLog(String s) {
165        if (sLocalLog != null) sLocalLog.log(mInterfaceName + ": " + s);
166    }
167
168
169
170    /*
171     * Driver and Supplicant management
172     */
173    private native static boolean loadDriverNative();
174    public boolean loadDriver() {
175        synchronized (sLock) {
176            return loadDriverNative();
177        }
178    }
179
180    private native static boolean isDriverLoadedNative();
181    public boolean isDriverLoaded() {
182        synchronized (sLock) {
183            return isDriverLoadedNative();
184        }
185    }
186
187    private native static boolean unloadDriverNative();
188    public boolean unloadDriver() {
189        synchronized (sLock) {
190            return unloadDriverNative();
191        }
192    }
193
194    private native static boolean startSupplicantNative(boolean p2pSupported);
195    public boolean startSupplicant(boolean p2pSupported) {
196        synchronized (sLock) {
197            return startSupplicantNative(p2pSupported);
198        }
199    }
200
201    /* Sends a kill signal to supplicant. To be used when we have lost connection
202       or when the supplicant is hung */
203    private native static boolean killSupplicantNative(boolean p2pSupported);
204    public boolean killSupplicant(boolean p2pSupported) {
205        synchronized (sLock) {
206            return killSupplicantNative(p2pSupported);
207        }
208    }
209
210    private native static boolean connectToSupplicantNative();
211    public boolean connectToSupplicant() {
212        synchronized (sLock) {
213            localLog(mInterfacePrefix + "connectToSupplicant");
214            return connectToSupplicantNative();
215        }
216    }
217
218    private native static void closeSupplicantConnectionNative();
219    public void closeSupplicantConnection() {
220        synchronized (sLock) {
221            localLog(mInterfacePrefix + "closeSupplicantConnection");
222            closeSupplicantConnectionNative();
223        }
224    }
225
226    /**
227     * Wait for the supplicant to send an event, returning the event string.
228     * @return the event string sent by the supplicant.
229     */
230    private native static String waitForEventNative();
231    public String waitForEvent() {
232        // No synchronization necessary .. it is implemented in WifiMonitor
233        return waitForEventNative();
234    }
235
236
237    /*
238     * Supplicant Command Primitives
239     */
240    private native boolean doBooleanCommandNative(String command);
241
242    private native int doIntCommandNative(String command);
243
244    private native String doStringCommandNative(String command);
245
246    private boolean doBooleanCommand(String command) {
247        if (DBG) Log.d(mTAG, "doBoolean: " + command);
248        synchronized (sLock) {
249            String toLog = mInterfacePrefix + command;
250            boolean result = doBooleanCommandNative(mInterfacePrefix + command);
251            localLog(toLog + " -> " + result);
252            if (DBG) Log.d(mTAG, command + ": returned " + result);
253            return result;
254        }
255    }
256
257    private boolean doBooleanCommandWithoutLogging(String command) {
258        if (DBG) Log.d(mTAG, "doBooleanCommandWithoutLogging: " + command);
259        synchronized (sLock) {
260            boolean result = doBooleanCommandNative(mInterfacePrefix + command);
261            if (DBG) Log.d(mTAG, command + ": returned " + result);
262            return result;
263        }
264    }
265
266    private int doIntCommand(String command) {
267        if (DBG) Log.d(mTAG, "doInt: " + command);
268        synchronized (sLock) {
269            String toLog = mInterfacePrefix + command;
270            int result = doIntCommandNative(mInterfacePrefix + command);
271            localLog(toLog + " -> " + result);
272            if (DBG) Log.d(mTAG, "   returned " + result);
273            return result;
274        }
275    }
276
277    private String doStringCommand(String command) {
278        if (DBG) {
279            //GET_NETWORK commands flood the logs
280            if (!command.startsWith("GET_NETWORK")) {
281                Log.d(mTAG, "doString: [" + command + "]");
282            }
283        }
284        synchronized (sLock) {
285            String toLog = mInterfacePrefix + command;
286            String result = doStringCommandNative(mInterfacePrefix + command);
287            if (result == null) {
288                if (DBG) Log.d(mTAG, "doStringCommandNative no result");
289            } else {
290                if (!command.startsWith("STATUS-")) {
291                    localLog(toLog + " -> " + result);
292                }
293                if (DBG) Log.d(mTAG, "   returned " + result.replace("\n", " "));
294            }
295            return result;
296        }
297    }
298
299    private String doStringCommandWithoutLogging(String command) {
300        if (DBG) {
301            //GET_NETWORK commands flood the logs
302            if (!command.startsWith("GET_NETWORK")) {
303                Log.d(mTAG, "doString: [" + command + "]");
304            }
305        }
306        synchronized (sLock) {
307            return doStringCommandNative(mInterfacePrefix + command);
308        }
309    }
310
311    public String doCustomSupplicantCommand(String command) {
312        return doStringCommand(command);
313    }
314
315    /*
316     * Wrappers for supplicant commands
317     */
318    public boolean ping() {
319        String pong = doStringCommand("PING");
320        return (pong != null && pong.equals("PONG"));
321    }
322
323    public void setSupplicantLogLevel(String level) {
324        doStringCommand("LOG_LEVEL " + level);
325    }
326
327    public String getFreqCapability() {
328        return doStringCommand("GET_CAPABILITY freq");
329    }
330
331    /**
332     * Create a comma separate string from integer set.
333     * @param values List of integers.
334     * @return comma separated string.
335     */
336    private static String createCSVStringFromIntegerSet(Set<Integer> values) {
337        StringBuilder list = new StringBuilder();
338        boolean first = true;
339        for (Integer value : values) {
340            if (!first) {
341                list.append(",");
342            }
343            list.append(value);
344            first = false;
345        }
346        return list.toString();
347    }
348
349    /**
350     * Start a scan using wpa_supplicant for the given frequencies.
351     * @param freqs list of frequencies to scan for, if null scan all supported channels.
352     * @param hiddenNetworkIds List of hidden networks to be scanned for.
353     */
354    public boolean scan(Set<Integer> freqs, Set<Integer> hiddenNetworkIds) {
355        String freqList = null;
356        String hiddenNetworkIdList = null;
357        if (freqs != null && freqs.size() != 0) {
358            freqList = createCSVStringFromIntegerSet(freqs);
359        }
360        if (hiddenNetworkIds != null && hiddenNetworkIds.size() != 0) {
361            hiddenNetworkIdList = createCSVStringFromIntegerSet(hiddenNetworkIds);
362        }
363        return scanWithParams(freqList, hiddenNetworkIdList);
364    }
365
366    private boolean scanWithParams(String freqList, String hiddenNetworkIdList) {
367        StringBuilder scanCommand = new StringBuilder();
368        scanCommand.append("SCAN TYPE=ONLY");
369        if (freqList != null) {
370            scanCommand.append(" freq=" + freqList);
371        }
372        if (hiddenNetworkIdList != null) {
373            scanCommand.append(" scan_id=" + hiddenNetworkIdList);
374        }
375        return doBooleanCommand(scanCommand.toString());
376    }
377
378    /* Does a graceful shutdown of supplicant. Is a common stop function for both p2p and sta.
379     *
380     * Note that underneath we use a harsh-sounding "terminate" supplicant command
381     * for a graceful stop and a mild-sounding "stop" interface
382     * to kill the process
383     */
384    public boolean stopSupplicant() {
385        return doBooleanCommand("TERMINATE");
386    }
387
388    public String listNetworks() {
389        return doStringCommand("LIST_NETWORKS");
390    }
391
392    public String listNetworks(int last_id) {
393        return doStringCommand("LIST_NETWORKS LAST_ID=" + last_id);
394    }
395
396    public int addNetwork() {
397        return doIntCommand("ADD_NETWORK");
398    }
399
400    public boolean setNetworkExtra(int netId, String name, Map<String, String> values) {
401        final String encoded;
402        try {
403            encoded = URLEncoder.encode(new JSONObject(values).toString(), "UTF-8");
404        } catch (NullPointerException e) {
405            Log.e(TAG, "Unable to serialize networkExtra: " + e.toString());
406            return false;
407        } catch (UnsupportedEncodingException e) {
408            Log.e(TAG, "Unable to serialize networkExtra: " + e.toString());
409            return false;
410        }
411        return setNetworkVariable(netId, name, "\"" + encoded + "\"");
412    }
413
414    public boolean setNetworkVariable(int netId, String name, String value) {
415        if (TextUtils.isEmpty(name) || TextUtils.isEmpty(value)) return false;
416        if (name.equals(WifiConfiguration.pskVarName)
417                || name.equals(WifiEnterpriseConfig.PASSWORD_KEY)) {
418            return doBooleanCommandWithoutLogging("SET_NETWORK " + netId + " " + name + " " + value);
419        } else {
420            return doBooleanCommand("SET_NETWORK " + netId + " " + name + " " + value);
421        }
422    }
423
424    public Map<String, String> getNetworkExtra(int netId, String name) {
425        final String wrapped = getNetworkVariable(netId, name);
426        if (wrapped == null || !wrapped.startsWith("\"") || !wrapped.endsWith("\"")) {
427            return null;
428        }
429        try {
430            final String encoded = wrapped.substring(1, wrapped.length() - 1);
431            // This method reads a JSON dictionary that was written by setNetworkExtra(). However,
432            // on devices that upgraded from Marshmallow, it may encounter a legacy value instead -
433            // an FQDN stored as a plain string. If such a value is encountered, the JSONObject
434            // constructor will thrown a JSONException and the method will return null.
435            final JSONObject json = new JSONObject(URLDecoder.decode(encoded, "UTF-8"));
436            final Map<String, String> values = new HashMap<String, String>();
437            final Iterator<?> it = json.keys();
438            while (it.hasNext()) {
439                final String key = (String) it.next();
440                final Object value = json.get(key);
441                if (value instanceof String) {
442                    values.put(key, (String) value);
443                }
444            }
445            return values;
446        } catch (UnsupportedEncodingException e) {
447            Log.e(TAG, "Unable to deserialize networkExtra: " + e.toString());
448            return null;
449        } catch (JSONException e) {
450            // This is not necessarily an error. This exception will also occur if we encounter a
451            // legacy FQDN stored as a plain string. We want to return null in this case as no JSON
452            // dictionary of extras was found.
453            return null;
454        }
455    }
456
457    public String getNetworkVariable(int netId, String name) {
458        if (TextUtils.isEmpty(name)) return null;
459
460        // GET_NETWORK will likely flood the logs ...
461        return doStringCommandWithoutLogging("GET_NETWORK " + netId + " " + name);
462    }
463
464    public boolean removeNetwork(int netId) {
465        return doBooleanCommand("REMOVE_NETWORK " + netId);
466    }
467
468
469    private void logDbg(String debug) {
470        long now = SystemClock.elapsedRealtimeNanos();
471        String ts = String.format("[%,d us] ", now/1000);
472        Log.e("WifiNative: ", ts+debug+ " stack:"
473                + Thread.currentThread().getStackTrace()[2].getMethodName() +" - "
474                + Thread.currentThread().getStackTrace()[3].getMethodName() +" - "
475                + Thread.currentThread().getStackTrace()[4].getMethodName() +" - "
476                + Thread.currentThread().getStackTrace()[5].getMethodName()+" - "
477                + Thread.currentThread().getStackTrace()[6].getMethodName());
478
479    }
480
481    /**
482     * Enables a network in wpa_supplicant.
483     * @param netId - Network ID of the network to be enabled.
484     * @return true if command succeeded, false otherwise.
485     */
486    public boolean enableNetwork(int netId) {
487        if (DBG) logDbg("enableNetwork nid=" + Integer.toString(netId));
488        return doBooleanCommand("ENABLE_NETWORK " + netId);
489    }
490
491    /**
492     * Enable a network in wpa_supplicant, do not connect.
493     * @param netId - Network ID of the network to be enabled.
494     * @return true if command succeeded, false otherwise.
495     */
496    public boolean enableNetworkWithoutConnect(int netId) {
497        if (DBG) logDbg("enableNetworkWithoutConnect nid=" + Integer.toString(netId));
498        return doBooleanCommand("ENABLE_NETWORK " + netId + " " + "no-connect");
499    }
500
501    /**
502     * Disables a network in wpa_supplicant.
503     * @param netId - Network ID of the network to be disabled.
504     * @return true if command succeeded, false otherwise.
505     */
506    public boolean disableNetwork(int netId) {
507        if (DBG) logDbg("disableNetwork nid=" + Integer.toString(netId));
508        return doBooleanCommand("DISABLE_NETWORK " + netId);
509    }
510
511    /**
512     * Select a network in wpa_supplicant (Disables all others).
513     * @param netId - Network ID of the network to be selected.
514     * @return true if command succeeded, false otherwise.
515     */
516    public boolean selectNetwork(int netId) {
517        if (DBG) logDbg("selectNetwork nid=" + Integer.toString(netId));
518        return doBooleanCommand("SELECT_NETWORK " + netId);
519    }
520
521    public boolean reconnect() {
522        if (DBG) logDbg("RECONNECT ");
523        return doBooleanCommand("RECONNECT");
524    }
525
526    public boolean reassociate() {
527        if (DBG) logDbg("REASSOCIATE ");
528        return doBooleanCommand("REASSOCIATE");
529    }
530
531    public boolean disconnect() {
532        if (DBG) logDbg("DISCONNECT ");
533        return doBooleanCommand("DISCONNECT");
534    }
535
536    public String status() {
537        return status(false);
538    }
539
540    public String status(boolean noEvents) {
541        if (noEvents) {
542            return doStringCommand("STATUS-NO_EVENTS");
543        } else {
544            return doStringCommand("STATUS");
545        }
546    }
547
548    public String getMacAddress() {
549        //Macaddr = XX.XX.XX.XX.XX.XX
550        String ret = doStringCommand("DRIVER MACADDR");
551        if (!TextUtils.isEmpty(ret)) {
552            String[] tokens = ret.split(" = ");
553            if (tokens.length == 2) return tokens[1];
554        }
555        return null;
556    }
557
558
559
560    /**
561     * Format of results:
562     * =================
563     * id=1
564     * bssid=68:7f:76:d7:1a:6e
565     * freq=2412
566     * level=-44
567     * tsf=1344626243700342
568     * flags=[WPA2-PSK-CCMP][WPS][ESS]
569     * ssid=zfdy
570     * ====
571     * id=2
572     * bssid=68:5f:74:d7:1a:6f
573     * freq=5180
574     * level=-73
575     * tsf=1344626243700373
576     * flags=[WPA2-PSK-CCMP][WPS][ESS]
577     * ssid=zuby
578     * ====
579     *
580     * RANGE=ALL gets all scan results
581     * RANGE=ID- gets results from ID
582     * MASK=<N> BSS command information mask.
583     *
584     * The mask used in this method, 0x29d87, gets the following fields:
585     *
586     *     WPA_BSS_MASK_ID         (Bit 0)
587     *     WPA_BSS_MASK_BSSID      (Bit 1)
588     *     WPA_BSS_MASK_FREQ       (Bit 2)
589     *     WPA_BSS_MASK_LEVEL      (Bit 7)
590     *     WPA_BSS_MASK_TSF        (Bit 8)
591     *     WPA_BSS_MASK_IE         (Bit 10)
592     *     WPA_BSS_MASK_FLAGS      (Bit 11)
593     *     WPA_BSS_MASK_SSID       (Bit 12)
594     *     WPA_BSS_MASK_INTERNETW  (Bit 15) (adds ANQP info)
595     *     WPA_BSS_MASK_DELIM      (Bit 17)
596     *
597     * See wpa_supplicant/src/common/wpa_ctrl.h for details.
598     */
599    private String getRawScanResults(String range) {
600        return doStringCommandWithoutLogging("BSS RANGE=" + range + " MASK=0x29d87");
601    }
602
603    private static final String BSS_IE_STR = "ie=";
604    private static final String BSS_ID_STR = "id=";
605    private static final String BSS_BSSID_STR = "bssid=";
606    private static final String BSS_FREQ_STR = "freq=";
607    private static final String BSS_LEVEL_STR = "level=";
608    private static final String BSS_TSF_STR = "tsf=";
609    private static final String BSS_FLAGS_STR = "flags=";
610    private static final String BSS_SSID_STR = "ssid=";
611    private static final String BSS_DELIMITER_STR = "====";
612    private static final String BSS_END_STR = "####";
613
614    public ArrayList<ScanDetail> getScanResults() {
615        int next_sid = 0;
616        ArrayList<ScanDetail> results = new ArrayList<>();
617        while(next_sid >= 0) {
618            String rawResult = getRawScanResults(next_sid+"-");
619            next_sid = -1;
620
621            if (TextUtils.isEmpty(rawResult))
622                break;
623
624            String[] lines = rawResult.split("\n");
625
626
627            // note that all these splits and substrings keep references to the original
628            // huge string buffer while the amount we really want is generally pretty small
629            // so make copies instead (one example b/11087956 wasted 400k of heap here).
630            final int bssidStrLen = BSS_BSSID_STR.length();
631            final int flagLen = BSS_FLAGS_STR.length();
632
633            String bssid = "";
634            int level = 0;
635            int freq = 0;
636            long tsf = 0;
637            String flags = "";
638            WifiSsid wifiSsid = null;
639            String infoElementsStr = null;
640            List<String> anqpLines = null;
641
642            for (String line : lines) {
643                if (line.startsWith(BSS_ID_STR)) { // Will find the last id line
644                    try {
645                        next_sid = Integer.parseInt(line.substring(BSS_ID_STR.length())) + 1;
646                    } catch (NumberFormatException e) {
647                        // Nothing to do
648                    }
649                } else if (line.startsWith(BSS_BSSID_STR)) {
650                    bssid = new String(line.getBytes(), bssidStrLen, line.length() - bssidStrLen);
651                } else if (line.startsWith(BSS_FREQ_STR)) {
652                    try {
653                        freq = Integer.parseInt(line.substring(BSS_FREQ_STR.length()));
654                    } catch (NumberFormatException e) {
655                        freq = 0;
656                    }
657                } else if (line.startsWith(BSS_LEVEL_STR)) {
658                    try {
659                        level = Integer.parseInt(line.substring(BSS_LEVEL_STR.length()));
660                        /* some implementations avoid negative values by adding 256
661                         * so we need to adjust for that here.
662                         */
663                        if (level > 0) level -= 256;
664                    } catch (NumberFormatException e) {
665                        level = 0;
666                    }
667                } else if (line.startsWith(BSS_TSF_STR)) {
668                    try {
669                        tsf = Long.parseLong(line.substring(BSS_TSF_STR.length()));
670                    } catch (NumberFormatException e) {
671                        tsf = 0;
672                    }
673                } else if (line.startsWith(BSS_FLAGS_STR)) {
674                    flags = new String(line.getBytes(), flagLen, line.length() - flagLen);
675                } else if (line.startsWith(BSS_SSID_STR)) {
676                    wifiSsid = WifiSsid.createFromAsciiEncoded(
677                            line.substring(BSS_SSID_STR.length()));
678                } else if (line.startsWith(BSS_IE_STR)) {
679                    infoElementsStr = line;
680                } else if (SupplicantBridge.isAnqpAttribute(line)) {
681                    if (anqpLines == null) {
682                        anqpLines = new ArrayList<>();
683                    }
684                    anqpLines.add(line);
685                } else if (line.startsWith(BSS_DELIMITER_STR) || line.startsWith(BSS_END_STR)) {
686                    if (bssid != null) {
687                        try {
688                            if (infoElementsStr == null) {
689                                throw new IllegalArgumentException("Null information element data");
690                            }
691                            int seperator = infoElementsStr.indexOf('=');
692                            if (seperator < 0) {
693                                throw new IllegalArgumentException("No element separator");
694                            }
695
696                            ScanResult.InformationElement[] infoElements =
697                                        InformationElementUtil.parseInformationElements(
698                                        Utils.hexToBytes(infoElementsStr.substring(seperator + 1)));
699
700                            NetworkDetail networkDetail = new NetworkDetail(bssid,
701                                    infoElements, anqpLines, freq);
702                            if (DBG) {
703                                Log.v(TAG + ":DTIM", "SSID" + networkDetail.getSSID()
704                                        + ", DTIM=" + networkDetail.getDtimInterval() + ", "
705                                        + " IEstr:" + infoElementsStr);
706                            }
707                            String xssid = (wifiSsid != null) ? wifiSsid.toString() : WifiSsid.NONE;
708                            if (!xssid.equals(networkDetail.getTrimmedSSID())) {
709                                Log.d(TAG, String.format(
710                                        "Inconsistent SSID on BSSID '%s': '%s' vs '%s': %s",
711                                        bssid, xssid, networkDetail.getSSID(), infoElementsStr));
712                            }
713
714                            if (networkDetail.hasInterworking()) {
715                                if (DBG) Log.d(TAG, "HSNwk: '" + networkDetail);
716                            }
717                            ScanDetail scan = new ScanDetail(networkDetail, wifiSsid, bssid, flags,
718                                    level, freq, tsf, infoElements, anqpLines);
719                            results.add(scan);
720                        } catch (IllegalArgumentException iae) {
721                            Log.d(TAG, "Failed to parse information elements: " + iae);
722                        }
723                    }
724                    bssid = null;
725                    level = 0;
726                    freq = 0;
727                    tsf = 0;
728                    flags = "";
729                    wifiSsid = null;
730                    infoElementsStr = null;
731                    anqpLines = null;
732                }
733            }
734        }
735        return results;
736    }
737
738    /**
739     * Format of result:
740     * id=1016
741     * bssid=00:03:7f:40:84:10
742     * freq=2462
743     * beacon_int=200
744     * capabilities=0x0431
745     * qual=0
746     * noise=0
747     * level=-46
748     * tsf=0000002669008476
749     * age=5
750     * ie=00105143412d485332302d52322d54455354010882848b960c12182403010b0706555...
751     * flags=[WPA2-EAP-CCMP][ESS][P2P][HS20]
752     * ssid=QCA-HS20-R2-TEST
753     * p2p_device_name=
754     * p2p_config_methods=0x0SET_NE
755     * anqp_venue_name=02083d656e6757692d466920416c6c69616e63650a3239383920436f...
756     * anqp_network_auth_type=010000
757     * anqp_roaming_consortium=03506f9a05001bc504bd
758     * anqp_ip_addr_type_availability=0c
759     * anqp_nai_realm=0200300000246d61696c2e6578616d706c652e636f6d3b636973636f2...
760     * anqp_3gpp=000600040132f465
761     * anqp_domain_name=0b65786d61706c652e636f6d
762     * hs20_operator_friendly_name=11656e6757692d466920416c6c69616e63650e636869...
763     * hs20_wan_metrics=01c40900008001000000000a00
764     * hs20_connection_capability=0100000006140001061600000650000106bb010106bb0...
765     * hs20_osu_providers_list=0b5143412d4f53552d425353010901310015656e6757692d...
766     */
767    public String scanResult(String bssid) {
768        return doStringCommand("BSS " + bssid);
769    }
770
771    public boolean startDriver() {
772        return doBooleanCommand("DRIVER START");
773    }
774
775    public boolean stopDriver() {
776        return doBooleanCommand("DRIVER STOP");
777    }
778
779
780    /**
781     * Start filtering out Multicast V4 packets
782     * @return {@code true} if the operation succeeded, {@code false} otherwise
783     *
784     * Multicast filtering rules work as follows:
785     *
786     * The driver can filter multicast (v4 and/or v6) and broadcast packets when in
787     * a power optimized mode (typically when screen goes off).
788     *
789     * In order to prevent the driver from filtering the multicast/broadcast packets, we have to
790     * add a DRIVER RXFILTER-ADD rule followed by DRIVER RXFILTER-START to make the rule effective
791     *
792     * DRIVER RXFILTER-ADD Num
793     *   where Num = 0 - Unicast, 1 - Broadcast, 2 - Mutil4 or 3 - Multi6
794     *
795     * and DRIVER RXFILTER-START
796     * In order to stop the usage of these rules, we do
797     *
798     * DRIVER RXFILTER-STOP
799     * DRIVER RXFILTER-REMOVE Num
800     *   where Num is as described for RXFILTER-ADD
801     *
802     * The  SETSUSPENDOPT driver command overrides the filtering rules
803     */
804    public boolean startFilteringMulticastV4Packets() {
805        return doBooleanCommand("DRIVER RXFILTER-STOP")
806            && doBooleanCommand("DRIVER RXFILTER-REMOVE 2")
807            && doBooleanCommand("DRIVER RXFILTER-START");
808    }
809
810    /**
811     * Stop filtering out Multicast V4 packets.
812     * @return {@code true} if the operation succeeded, {@code false} otherwise
813     */
814    public boolean stopFilteringMulticastV4Packets() {
815        return doBooleanCommand("DRIVER RXFILTER-STOP")
816            && doBooleanCommand("DRIVER RXFILTER-ADD 2")
817            && doBooleanCommand("DRIVER RXFILTER-START");
818    }
819
820    /**
821     * Start filtering out Multicast V6 packets
822     * @return {@code true} if the operation succeeded, {@code false} otherwise
823     */
824    public boolean startFilteringMulticastV6Packets() {
825        return doBooleanCommand("DRIVER RXFILTER-STOP")
826            && doBooleanCommand("DRIVER RXFILTER-REMOVE 3")
827            && doBooleanCommand("DRIVER RXFILTER-START");
828    }
829
830    /**
831     * Stop filtering out Multicast V6 packets.
832     * @return {@code true} if the operation succeeded, {@code false} otherwise
833     */
834    public boolean stopFilteringMulticastV6Packets() {
835        return doBooleanCommand("DRIVER RXFILTER-STOP")
836            && doBooleanCommand("DRIVER RXFILTER-ADD 3")
837            && doBooleanCommand("DRIVER RXFILTER-START");
838    }
839
840    /**
841     * Set the operational frequency band
842     * @param band One of
843     *     {@link WifiManager#WIFI_FREQUENCY_BAND_AUTO},
844     *     {@link WifiManager#WIFI_FREQUENCY_BAND_5GHZ},
845     *     {@link WifiManager#WIFI_FREQUENCY_BAND_2GHZ},
846     * @return {@code true} if the operation succeeded, {@code false} otherwise
847     */
848    public boolean setBand(int band) {
849        String bandstr;
850
851        if (band == WifiManager.WIFI_FREQUENCY_BAND_5GHZ)
852            bandstr = "5G";
853        else if (band == WifiManager.WIFI_FREQUENCY_BAND_2GHZ)
854            bandstr = "2G";
855        else
856            bandstr = "AUTO";
857        return doBooleanCommand("SET SETBAND " + bandstr);
858    }
859
860    public static final int BLUETOOTH_COEXISTENCE_MODE_ENABLED     = 0;
861    public static final int BLUETOOTH_COEXISTENCE_MODE_DISABLED    = 1;
862    public static final int BLUETOOTH_COEXISTENCE_MODE_SENSE       = 2;
863    /**
864      * Sets the bluetooth coexistence mode.
865      *
866      * @param mode One of {@link #BLUETOOTH_COEXISTENCE_MODE_DISABLED},
867      *            {@link #BLUETOOTH_COEXISTENCE_MODE_ENABLED}, or
868      *            {@link #BLUETOOTH_COEXISTENCE_MODE_SENSE}.
869      * @return Whether the mode was successfully set.
870      */
871    public boolean setBluetoothCoexistenceMode(int mode) {
872        return doBooleanCommand("DRIVER BTCOEXMODE " + mode);
873    }
874
875    /**
876     * Enable or disable Bluetooth coexistence scan mode. When this mode is on,
877     * some of the low-level scan parameters used by the driver are changed to
878     * reduce interference with A2DP streaming.
879     *
880     * @param isSet whether to enable or disable this mode
881     * @return {@code true} if the command succeeded, {@code false} otherwise.
882     */
883    public boolean setBluetoothCoexistenceScanMode(boolean setCoexScanMode) {
884        if (setCoexScanMode) {
885            return doBooleanCommand("DRIVER BTCOEXSCAN-START");
886        } else {
887            return doBooleanCommand("DRIVER BTCOEXSCAN-STOP");
888        }
889    }
890
891    public void enableSaveConfig() {
892        doBooleanCommand("SET update_config 1");
893    }
894
895    public boolean saveConfig() {
896        return doBooleanCommand("SAVE_CONFIG");
897    }
898
899    public boolean addToBlacklist(String bssid) {
900        if (TextUtils.isEmpty(bssid)) return false;
901        return doBooleanCommand("BLACKLIST " + bssid);
902    }
903
904    public boolean clearBlacklist() {
905        return doBooleanCommand("BLACKLIST clear");
906    }
907
908    public boolean setSuspendOptimizations(boolean enabled) {
909        if (enabled) {
910            return doBooleanCommand("DRIVER SETSUSPENDMODE 1");
911        } else {
912            return doBooleanCommand("DRIVER SETSUSPENDMODE 0");
913        }
914    }
915
916    public boolean setCountryCode(String countryCode) {
917        if (countryCode != null)
918            return doBooleanCommand("DRIVER COUNTRY " + countryCode.toUpperCase(Locale.ROOT));
919        else
920            return doBooleanCommand("DRIVER COUNTRY");
921    }
922
923    /**
924     * Start/Stop PNO scan.
925     * @param enable boolean indicating whether PNO is being enabled or disabled.
926     */
927    public boolean setPnoScan(boolean enable) {
928        String cmd = enable ? "SET pno 1" : "SET pno 0";
929        return doBooleanCommand(cmd);
930    }
931
932    // TODO(rpius): Move PnoMonitor to SupplicantWifiScannerImpl.
933    //PNO Monitor
934    private class PnoMonitor {
935        private static final int MINIMUM_PNO_GAP = 5 * 1000;
936        private static final String ACTION_TOGGLE_PNO =
937            "com.android.server.Wifi.action.TOGGLE_PNO";
938        long mLastPnoChangeTimeStamp = -1L;
939        boolean mExpectedPnoState = false;
940        List<PnoNetwork> mExpectedPnoNetworkList = null;
941        boolean mCurrentPnoState = false;;
942        boolean mWaitForTimer = false;
943        private boolean mIsPaused = false;
944        final Object mPnoLock = new Object();
945        private final AlarmManager mAlarmManager =
946                (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
947        private final PendingIntent mPnoIntent;
948
949        public PnoMonitor() {
950            Intent intent = new Intent(ACTION_TOGGLE_PNO, null);
951            intent.setPackage("android");
952            mPnoIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
953
954            mContext.registerReceiver(
955                new BroadcastReceiver() {
956                    @Override
957                    public void onReceive(Context context, Intent intent) {
958                        synchronized(mPnoLock) {
959                            if (DBG) {
960                                Log.d(mTAG, "PNO timer expire, PNO should change to "
961                                        + mExpectedPnoState);
962                            }
963                            boolean ret = setPno(mExpectedPnoState, mExpectedPnoNetworkList);
964                            if (!ret) {
965                                Log.e(mTAG, "set PNO failure");
966                            }
967                            mWaitForTimer = false;
968                        }
969                    }
970                },
971                new IntentFilter(ACTION_TOGGLE_PNO));
972        }
973
974        /**
975         * Enable/Disable PNO with updated network priorities.
976         * @param enable boolean indicating whether PNO is being enabled or disabled.
977         * @param pnoNetworkList list of networks with priorities to be set before PNO setting.
978         */
979        private boolean setPno(boolean enable, List<PnoNetwork> pnoNetworkList) {
980            if (mIsPaused) {
981                if (DBG) Log.d(mTAG, "Do not change PNO state since it is paused");
982                return true;
983            }
984            if (mCurrentPnoState == enable) {
985                if (DBG) Log.d(mTAG, "Do not change PNO since current is expected");
986                return true;
987            }
988            if (DBG) {
989                Log.d(mTAG, "change PNO from " + mCurrentPnoState + " to " + enable);
990            }
991            // TODO: Couple of cases yet to be handled:
992            // 1. What if the network priority update fails, should we bail out of PNO setting?
993            // 2. If PNO setting fails below, should we go back and revert this priority change?
994            if (pnoNetworkList != null) {
995                if (DBG) Log.i(mTAG, "update priorities for PNO. Enable: " + enable);
996                for (PnoNetwork pnoNetwork : pnoNetworkList) {
997                    // What if this fails? Should we bail out?
998                    boolean isSuccess = setNetworkVariable(pnoNetwork.networkId,
999                            WifiConfiguration.priorityVarName,
1000                            Integer.toString(pnoNetwork.priority));
1001                    if (!isSuccess) {
1002                        Log.e(mTAG, "update priority failed for :" + pnoNetwork.networkId);
1003                    }
1004                }
1005            }
1006            boolean ret = WifiNative.this.setPnoScan(enable);
1007            mLastPnoChangeTimeStamp = System.currentTimeMillis();
1008            if (ret) {
1009                mCurrentPnoState = enable;
1010            }
1011            return ret;
1012        }
1013
1014        public boolean enableBackgroundScan(
1015                boolean enable,
1016                List<PnoNetwork> pnoNetworkList) {
1017            synchronized(mPnoLock) {
1018                Log.i(mTAG, "set expected PNO to " +  enable);
1019                mExpectedPnoState = enable;
1020                mExpectedPnoNetworkList = pnoNetworkList;
1021                if (mWaitForTimer) {
1022                    //already has a timer
1023                    if (DBG) Log.d(mTAG, "update expected PNO to " +  mExpectedPnoState);
1024                } else {
1025                    long timeDifference = System.currentTimeMillis() - mLastPnoChangeTimeStamp;
1026                    if (timeDifference >= MINIMUM_PNO_GAP) {
1027                        return setPno(enable, pnoNetworkList);
1028                    } else {
1029                        mWaitForTimer = true;
1030                        if (DBG) Log.d(mTAG, "start PNO timer with delay:" + timeDifference);
1031                        mAlarmManager.set(AlarmManager.RTC_WAKEUP,
1032                                System.currentTimeMillis() + timeDifference, mPnoIntent);
1033                    }
1034                }
1035                return true;
1036            }
1037        }
1038
1039        /**
1040         * Pause PNO scanning.
1041         */
1042        public void pauseBackgroundScan() {
1043            synchronized (mPnoLock) {
1044                if (!mIsPaused) {
1045                    Log.i(mTAG, "Pausing Pno scan. Current state: " + mCurrentPnoState);
1046                    if (mCurrentPnoState) {
1047                        setPno(false, null);
1048                    }
1049                    mIsPaused = true;
1050                }
1051            }
1052        }
1053
1054        /**
1055         * Resume PNO scanning.
1056         */
1057        public void resumeBackgroundScan() {
1058            synchronized (mPnoLock) {
1059                if (mIsPaused) {
1060                    Log.i(mTAG, "Resuming Pno scan. Expected state: " + mExpectedPnoState);
1061                    mIsPaused = false;
1062                    if (mExpectedPnoState) {
1063                        setPno(true, null);
1064                    }
1065                }
1066            }
1067        }
1068    }
1069
1070    public boolean enableBackgroundScan(
1071            boolean enable,
1072            List<PnoNetwork> pnoNetworkList) {
1073        if (mPnoMonitor != null) {
1074            return mPnoMonitor.enableBackgroundScan(enable, pnoNetworkList);
1075        } else {
1076            return false;
1077        }
1078    }
1079
1080    /**
1081     * Pause PNO scanning.
1082     * Note: If Pno is currently running, the stop might take a little while to take effect!
1083     */
1084    public void pauseBackgroundScan() {
1085        if (mPnoMonitor != null) {
1086            mPnoMonitor.pauseBackgroundScan();
1087        }
1088    }
1089
1090    /**
1091     * Resume PNO scanning.
1092     */
1093    public void resumeBackgroundScan() {
1094        if (mPnoMonitor != null) {
1095            mPnoMonitor.resumeBackgroundScan();
1096        }
1097    }
1098
1099    public void enableAutoConnect(boolean enable) {
1100        if (enable) {
1101            doBooleanCommand("STA_AUTOCONNECT 1");
1102        } else {
1103            doBooleanCommand("STA_AUTOCONNECT 0");
1104        }
1105    }
1106
1107    public void setScanInterval(int scanInterval) {
1108        doBooleanCommand("SCAN_INTERVAL " + scanInterval);
1109    }
1110
1111    public void setHs20(boolean hs20) {
1112        if (hs20) {
1113            doBooleanCommand("SET HS20 1");
1114        } else {
1115            doBooleanCommand("SET HS20 0");
1116        }
1117    }
1118
1119    public void startTdls(String macAddr, boolean enable) {
1120        if (enable) {
1121            synchronized (sLock) {
1122                doBooleanCommand("TDLS_DISCOVER " + macAddr);
1123                doBooleanCommand("TDLS_SETUP " + macAddr);
1124            }
1125        } else {
1126            doBooleanCommand("TDLS_TEARDOWN " + macAddr);
1127        }
1128    }
1129
1130    /** Example output:
1131     * RSSI=-65
1132     * LINKSPEED=48
1133     * NOISE=9999
1134     * FREQUENCY=0
1135     */
1136    public String signalPoll() {
1137        return doStringCommandWithoutLogging("SIGNAL_POLL");
1138    }
1139
1140    /** Example outout:
1141     * TXGOOD=396
1142     * TXBAD=1
1143     */
1144    public String pktcntPoll() {
1145        return doStringCommand("PKTCNT_POLL");
1146    }
1147
1148    public void bssFlush() {
1149        doBooleanCommand("BSS_FLUSH 0");
1150    }
1151
1152    public boolean startWpsPbc(String bssid) {
1153        if (TextUtils.isEmpty(bssid)) {
1154            return doBooleanCommand("WPS_PBC");
1155        } else {
1156            return doBooleanCommand("WPS_PBC " + bssid);
1157        }
1158    }
1159
1160    public boolean startWpsPbc(String iface, String bssid) {
1161        synchronized (sLock) {
1162            if (TextUtils.isEmpty(bssid)) {
1163                return doBooleanCommandNative("IFNAME=" + iface + " WPS_PBC");
1164            } else {
1165                return doBooleanCommandNative("IFNAME=" + iface + " WPS_PBC " + bssid);
1166            }
1167        }
1168    }
1169
1170    public boolean startWpsPinKeypad(String pin) {
1171        if (TextUtils.isEmpty(pin)) return false;
1172        return doBooleanCommand("WPS_PIN any " + pin);
1173    }
1174
1175    public boolean startWpsPinKeypad(String iface, String pin) {
1176        if (TextUtils.isEmpty(pin)) return false;
1177        synchronized (sLock) {
1178            return doBooleanCommandNative("IFNAME=" + iface + " WPS_PIN any " + pin);
1179        }
1180    }
1181
1182
1183    public String startWpsPinDisplay(String bssid) {
1184        if (TextUtils.isEmpty(bssid)) {
1185            return doStringCommand("WPS_PIN any");
1186        } else {
1187            return doStringCommand("WPS_PIN " + bssid);
1188        }
1189    }
1190
1191    public String startWpsPinDisplay(String iface, String bssid) {
1192        synchronized (sLock) {
1193            if (TextUtils.isEmpty(bssid)) {
1194                return doStringCommandNative("IFNAME=" + iface + " WPS_PIN any");
1195            } else {
1196                return doStringCommandNative("IFNAME=" + iface + " WPS_PIN " + bssid);
1197            }
1198        }
1199    }
1200
1201    public boolean setExternalSim(boolean external) {
1202        String value = external ? "1" : "0";
1203        Log.d(TAG, "Setting external_sim to " + value);
1204        return doBooleanCommand("SET external_sim " + value);
1205    }
1206
1207    public boolean simAuthResponse(int id, String type, String response) {
1208        // with type = GSM-AUTH, UMTS-AUTH or UMTS-AUTS
1209        return doBooleanCommand("CTRL-RSP-SIM-" + id + ":" + type + response);
1210    }
1211
1212    public boolean simAuthFailedResponse(int id) {
1213        // should be used with type GSM-AUTH
1214        return doBooleanCommand("CTRL-RSP-SIM-" + id + ":GSM-FAIL");
1215    }
1216
1217    public boolean umtsAuthFailedResponse(int id) {
1218        // should be used with type UMTS-AUTH
1219        return doBooleanCommand("CTRL-RSP-SIM-" + id + ":UMTS-FAIL");
1220    }
1221
1222    public boolean simIdentityResponse(int id, String response) {
1223        return doBooleanCommand("CTRL-RSP-IDENTITY-" + id + ":" + response);
1224    }
1225
1226    /* Configures an access point connection */
1227    public boolean startWpsRegistrar(String bssid, String pin) {
1228        if (TextUtils.isEmpty(bssid) || TextUtils.isEmpty(pin)) return false;
1229        return doBooleanCommand("WPS_REG " + bssid + " " + pin);
1230    }
1231
1232    public boolean cancelWps() {
1233        return doBooleanCommand("WPS_CANCEL");
1234    }
1235
1236    public boolean setPersistentReconnect(boolean enabled) {
1237        int value = (enabled == true) ? 1 : 0;
1238        return doBooleanCommand("SET persistent_reconnect " + value);
1239    }
1240
1241    public boolean setDeviceName(String name) {
1242        return doBooleanCommand("SET device_name " + name);
1243    }
1244
1245    public boolean setDeviceType(String type) {
1246        return doBooleanCommand("SET device_type " + type);
1247    }
1248
1249    public boolean setConfigMethods(String cfg) {
1250        return doBooleanCommand("SET config_methods " + cfg);
1251    }
1252
1253    public boolean setManufacturer(String value) {
1254        return doBooleanCommand("SET manufacturer " + value);
1255    }
1256
1257    public boolean setModelName(String value) {
1258        return doBooleanCommand("SET model_name " + value);
1259    }
1260
1261    public boolean setModelNumber(String value) {
1262        return doBooleanCommand("SET model_number " + value);
1263    }
1264
1265    public boolean setSerialNumber(String value) {
1266        return doBooleanCommand("SET serial_number " + value);
1267    }
1268
1269    public boolean setP2pSsidPostfix(String postfix) {
1270        return doBooleanCommand("SET p2p_ssid_postfix " + postfix);
1271    }
1272
1273    public boolean setP2pGroupIdle(String iface, int time) {
1274        synchronized (sLock) {
1275            return doBooleanCommandNative("IFNAME=" + iface + " SET p2p_group_idle " + time);
1276        }
1277    }
1278
1279    public void setPowerSave(boolean enabled) {
1280        if (enabled) {
1281            doBooleanCommand("SET ps 1");
1282        } else {
1283            doBooleanCommand("SET ps 0");
1284        }
1285    }
1286
1287    public boolean setP2pPowerSave(String iface, boolean enabled) {
1288        synchronized (sLock) {
1289            if (enabled) {
1290                return doBooleanCommandNative("IFNAME=" + iface + " P2P_SET ps 1");
1291            } else {
1292                return doBooleanCommandNative("IFNAME=" + iface + " P2P_SET ps 0");
1293            }
1294        }
1295    }
1296
1297    public boolean setWfdEnable(boolean enable) {
1298        return doBooleanCommand("SET wifi_display " + (enable ? "1" : "0"));
1299    }
1300
1301    public boolean setWfdDeviceInfo(String hex) {
1302        return doBooleanCommand("WFD_SUBELEM_SET 0 " + hex);
1303    }
1304
1305    /**
1306     * "sta" prioritizes STA connection over P2P and "p2p" prioritizes
1307     * P2P connection over STA
1308     */
1309    public boolean setConcurrencyPriority(String s) {
1310        return doBooleanCommand("P2P_SET conc_pref " + s);
1311    }
1312
1313    public boolean p2pFind() {
1314        return doBooleanCommand("P2P_FIND");
1315    }
1316
1317    public boolean p2pFind(int timeout) {
1318        if (timeout <= 0) {
1319            return p2pFind();
1320        }
1321        return doBooleanCommand("P2P_FIND " + timeout);
1322    }
1323
1324    public boolean p2pStopFind() {
1325       return doBooleanCommand("P2P_STOP_FIND");
1326    }
1327
1328    public boolean p2pListen() {
1329        return doBooleanCommand("P2P_LISTEN");
1330    }
1331
1332    public boolean p2pListen(int timeout) {
1333        if (timeout <= 0) {
1334            return p2pListen();
1335        }
1336        return doBooleanCommand("P2P_LISTEN " + timeout);
1337    }
1338
1339    public boolean p2pExtListen(boolean enable, int period, int interval) {
1340        if (enable && interval < period) {
1341            return false;
1342        }
1343        return doBooleanCommand("P2P_EXT_LISTEN"
1344                    + (enable ? (" " + period + " " + interval) : ""));
1345    }
1346
1347    public boolean p2pSetChannel(int lc, int oc) {
1348        if (DBG) Log.d(mTAG, "p2pSetChannel: lc="+lc+", oc="+oc);
1349
1350        synchronized (sLock) {
1351            if (lc >=1 && lc <= 11) {
1352                if (!doBooleanCommand("P2P_SET listen_channel " + lc)) {
1353                    return false;
1354                }
1355            } else if (lc != 0) {
1356                return false;
1357            }
1358
1359            if (oc >= 1 && oc <= 165 ) {
1360                int freq = (oc <= 14 ? 2407 : 5000) + oc * 5;
1361                return doBooleanCommand("P2P_SET disallow_freq 1000-"
1362                        + (freq - 5) + "," + (freq + 5) + "-6000");
1363            } else if (oc == 0) {
1364                /* oc==0 disables "P2P_SET disallow_freq" (enables all freqs) */
1365                return doBooleanCommand("P2P_SET disallow_freq \"\"");
1366            }
1367        }
1368        return false;
1369    }
1370
1371    public boolean p2pFlush() {
1372        return doBooleanCommand("P2P_FLUSH");
1373    }
1374
1375    private static final int DEFAULT_GROUP_OWNER_INTENT     = 6;
1376    /* p2p_connect <peer device address> <pbc|pin|PIN#> [label|display|keypad]
1377        [persistent] [join|auth] [go_intent=<0..15>] [freq=<in MHz>] */
1378    public String p2pConnect(WifiP2pConfig config, boolean joinExistingGroup) {
1379        if (config == null) return null;
1380        List<String> args = new ArrayList<String>();
1381        WpsInfo wps = config.wps;
1382        args.add(config.deviceAddress);
1383
1384        switch (wps.setup) {
1385            case WpsInfo.PBC:
1386                args.add("pbc");
1387                break;
1388            case WpsInfo.DISPLAY:
1389                if (TextUtils.isEmpty(wps.pin)) {
1390                    args.add("pin");
1391                } else {
1392                    args.add(wps.pin);
1393                }
1394                args.add("display");
1395                break;
1396            case WpsInfo.KEYPAD:
1397                args.add(wps.pin);
1398                args.add("keypad");
1399                break;
1400            case WpsInfo.LABEL:
1401                args.add(wps.pin);
1402                args.add("label");
1403            default:
1404                break;
1405        }
1406
1407        if (config.netId == WifiP2pGroup.PERSISTENT_NET_ID) {
1408            args.add("persistent");
1409        }
1410
1411        if (joinExistingGroup) {
1412            args.add("join");
1413        } else {
1414            //TODO: This can be adapted based on device plugged in state and
1415            //device battery state
1416            int groupOwnerIntent = config.groupOwnerIntent;
1417            if (groupOwnerIntent < 0 || groupOwnerIntent > 15) {
1418                groupOwnerIntent = DEFAULT_GROUP_OWNER_INTENT;
1419            }
1420            args.add("go_intent=" + groupOwnerIntent);
1421        }
1422
1423        String command = "P2P_CONNECT ";
1424        for (String s : args) command += s + " ";
1425
1426        return doStringCommand(command);
1427    }
1428
1429    public boolean p2pCancelConnect() {
1430        return doBooleanCommand("P2P_CANCEL");
1431    }
1432
1433    public boolean p2pProvisionDiscovery(WifiP2pConfig config) {
1434        if (config == null) return false;
1435
1436        switch (config.wps.setup) {
1437            case WpsInfo.PBC:
1438                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " pbc");
1439            case WpsInfo.DISPLAY:
1440                //We are doing display, so provision discovery is keypad
1441                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " keypad");
1442            case WpsInfo.KEYPAD:
1443                //We are doing keypad, so provision discovery is display
1444                return doBooleanCommand("P2P_PROV_DISC " + config.deviceAddress + " display");
1445            default:
1446                break;
1447        }
1448        return false;
1449    }
1450
1451    public boolean p2pGroupAdd(boolean persistent) {
1452        if (persistent) {
1453            return doBooleanCommand("P2P_GROUP_ADD persistent");
1454        }
1455        return doBooleanCommand("P2P_GROUP_ADD");
1456    }
1457
1458    public boolean p2pGroupAdd(int netId) {
1459        return doBooleanCommand("P2P_GROUP_ADD persistent=" + netId);
1460    }
1461
1462    public boolean p2pGroupRemove(String iface) {
1463        if (TextUtils.isEmpty(iface)) return false;
1464        synchronized (sLock) {
1465            return doBooleanCommandNative("IFNAME=" + iface + " P2P_GROUP_REMOVE " + iface);
1466        }
1467    }
1468
1469    public boolean p2pReject(String deviceAddress) {
1470        return doBooleanCommand("P2P_REJECT " + deviceAddress);
1471    }
1472
1473    /* Invite a peer to a group */
1474    public boolean p2pInvite(WifiP2pGroup group, String deviceAddress) {
1475        if (TextUtils.isEmpty(deviceAddress)) return false;
1476
1477        if (group == null) {
1478            return doBooleanCommand("P2P_INVITE peer=" + deviceAddress);
1479        } else {
1480            return doBooleanCommand("P2P_INVITE group=" + group.getInterface()
1481                    + " peer=" + deviceAddress + " go_dev_addr=" + group.getOwner().deviceAddress);
1482        }
1483    }
1484
1485    /* Reinvoke a persistent connection */
1486    public boolean p2pReinvoke(int netId, String deviceAddress) {
1487        if (TextUtils.isEmpty(deviceAddress) || netId < 0) return false;
1488
1489        return doBooleanCommand("P2P_INVITE persistent=" + netId + " peer=" + deviceAddress);
1490    }
1491
1492    public String p2pGetSsid(String deviceAddress) {
1493        return p2pGetParam(deviceAddress, "oper_ssid");
1494    }
1495
1496    public String p2pGetDeviceAddress() {
1497        Log.d(TAG, "p2pGetDeviceAddress");
1498
1499        String status = null;
1500
1501        /* Explicitly calling the API without IFNAME= prefix to take care of the devices that
1502        don't have p2p0 interface. Supplicant seems to be returning the correct address anyway. */
1503
1504        synchronized (sLock) {
1505            status = doStringCommandNative("STATUS");
1506        }
1507
1508        String result = "";
1509        if (status != null) {
1510            String[] tokens = status.split("\n");
1511            for (String token : tokens) {
1512                if (token.startsWith("p2p_device_address=")) {
1513                    String[] nameValue = token.split("=");
1514                    if (nameValue.length != 2)
1515                        break;
1516                    result = nameValue[1];
1517                }
1518            }
1519        }
1520
1521        Log.d(TAG, "p2pGetDeviceAddress returning " + result);
1522        return result;
1523    }
1524
1525    public int getGroupCapability(String deviceAddress) {
1526        int gc = 0;
1527        if (TextUtils.isEmpty(deviceAddress)) return gc;
1528        String peerInfo = p2pPeer(deviceAddress);
1529        if (TextUtils.isEmpty(peerInfo)) return gc;
1530
1531        String[] tokens = peerInfo.split("\n");
1532        for (String token : tokens) {
1533            if (token.startsWith("group_capab=")) {
1534                String[] nameValue = token.split("=");
1535                if (nameValue.length != 2) break;
1536                try {
1537                    return Integer.decode(nameValue[1]);
1538                } catch(NumberFormatException e) {
1539                    return gc;
1540                }
1541            }
1542        }
1543        return gc;
1544    }
1545
1546    public String p2pPeer(String deviceAddress) {
1547        return doStringCommand("P2P_PEER " + deviceAddress);
1548    }
1549
1550    private String p2pGetParam(String deviceAddress, String key) {
1551        if (deviceAddress == null) return null;
1552
1553        String peerInfo = p2pPeer(deviceAddress);
1554        if (peerInfo == null) return null;
1555        String[] tokens= peerInfo.split("\n");
1556
1557        key += "=";
1558        for (String token : tokens) {
1559            if (token.startsWith(key)) {
1560                String[] nameValue = token.split("=");
1561                if (nameValue.length != 2) break;
1562                return nameValue[1];
1563            }
1564        }
1565        return null;
1566    }
1567
1568    public boolean p2pServiceAdd(WifiP2pServiceInfo servInfo) {
1569        /*
1570         * P2P_SERVICE_ADD bonjour <query hexdump> <RDATA hexdump>
1571         * P2P_SERVICE_ADD upnp <version hex> <service>
1572         *
1573         * e.g)
1574         * [Bonjour]
1575         * # IP Printing over TCP (PTR) (RDATA=MyPrinter._ipp._tcp.local.)
1576         * P2P_SERVICE_ADD bonjour 045f697070c00c000c01 094d795072696e746572c027
1577         * # IP Printing over TCP (TXT) (RDATA=txtvers=1,pdl=application/postscript)
1578         * P2P_SERVICE_ADD bonjour 096d797072696e746572045f697070c00c001001
1579         *  09747874766572733d311a70646c3d6170706c69636174696f6e2f706f7374736372797074
1580         *
1581         * [UPnP]
1582         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012
1583         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice
1584         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::urn:schemas-upnp
1585         * -org:device:InternetGatewayDevice:1
1586         * P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9322-123456789012::urn:schemas-upnp
1587         * -org:service:ContentDirectory:2
1588         */
1589        synchronized (sLock) {
1590            for (String s : servInfo.getSupplicantQueryList()) {
1591                String command = "P2P_SERVICE_ADD";
1592                command += (" " + s);
1593                if (!doBooleanCommand(command)) {
1594                    return false;
1595                }
1596            }
1597        }
1598        return true;
1599    }
1600
1601    public boolean p2pServiceDel(WifiP2pServiceInfo servInfo) {
1602        /*
1603         * P2P_SERVICE_DEL bonjour <query hexdump>
1604         * P2P_SERVICE_DEL upnp <version hex> <service>
1605         */
1606        synchronized (sLock) {
1607            for (String s : servInfo.getSupplicantQueryList()) {
1608                String command = "P2P_SERVICE_DEL ";
1609
1610                String[] data = s.split(" ");
1611                if (data.length < 2) {
1612                    return false;
1613                }
1614                if ("upnp".equals(data[0])) {
1615                    command += s;
1616                } else if ("bonjour".equals(data[0])) {
1617                    command += data[0];
1618                    command += (" " + data[1]);
1619                } else {
1620                    return false;
1621                }
1622                if (!doBooleanCommand(command)) {
1623                    return false;
1624                }
1625            }
1626        }
1627        return true;
1628    }
1629
1630    public boolean p2pServiceFlush() {
1631        return doBooleanCommand("P2P_SERVICE_FLUSH");
1632    }
1633
1634    public String p2pServDiscReq(String addr, String query) {
1635        String command = "P2P_SERV_DISC_REQ";
1636        command += (" " + addr);
1637        command += (" " + query);
1638
1639        return doStringCommand(command);
1640    }
1641
1642    public boolean p2pServDiscCancelReq(String id) {
1643        return doBooleanCommand("P2P_SERV_DISC_CANCEL_REQ " + id);
1644    }
1645
1646    /* Set the current mode of miracast operation.
1647     *  0 = disabled
1648     *  1 = operating as source
1649     *  2 = operating as sink
1650     */
1651    public void setMiracastMode(int mode) {
1652        // Note: optional feature on the driver. It is ok for this to fail.
1653        doBooleanCommand("DRIVER MIRACAST " + mode);
1654    }
1655
1656    public boolean fetchAnqp(String bssid, String subtypes) {
1657        return doBooleanCommand("ANQP_GET " + bssid + " " + subtypes);
1658    }
1659
1660    /*
1661     * NFC-related calls
1662     */
1663    public String getNfcWpsConfigurationToken(int netId) {
1664        return doStringCommand("WPS_NFC_CONFIG_TOKEN WPS " + netId);
1665    }
1666
1667    public String getNfcHandoverRequest() {
1668        return doStringCommand("NFC_GET_HANDOVER_REQ NDEF P2P-CR");
1669    }
1670
1671    public String getNfcHandoverSelect() {
1672        return doStringCommand("NFC_GET_HANDOVER_SEL NDEF P2P-CR");
1673    }
1674
1675    public boolean initiatorReportNfcHandover(String selectMessage) {
1676        return doBooleanCommand("NFC_REPORT_HANDOVER INIT P2P 00 " + selectMessage);
1677    }
1678
1679    public boolean responderReportNfcHandover(String requestMessage) {
1680        return doBooleanCommand("NFC_REPORT_HANDOVER RESP P2P " + requestMessage + " 00");
1681    }
1682
1683
1684    /* kernel logging support */
1685    private static native byte[] readKernelLogNative();
1686
1687    synchronized public String readKernelLog() {
1688        byte[] bytes = readKernelLogNative();
1689        if (bytes != null) {
1690            CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
1691            try {
1692                CharBuffer decoded = decoder.decode(ByteBuffer.wrap(bytes));
1693                return decoded.toString();
1694            } catch (CharacterCodingException cce) {
1695                return new String(bytes, StandardCharsets.ISO_8859_1);
1696            }
1697        } else {
1698            return "*** failed to read kernel log ***";
1699        }
1700    }
1701
1702    /* WIFI HAL support */
1703
1704    // HAL command ids
1705    private static int sCmdId = 1;
1706    private static int getNewCmdIdLocked() {
1707        return sCmdId++;
1708    }
1709
1710    private static final String TAG = "WifiNative-HAL";
1711    private static long sWifiHalHandle = 0;             /* used by JNI to save wifi_handle */
1712    private static long[] sWifiIfaceHandles = null;     /* used by JNI to save interface handles */
1713    public static int sWlan0Index = -1;
1714    private static MonitorThread sThread;
1715    private static final int STOP_HAL_TIMEOUT_MS = 1000;
1716
1717    private static native boolean startHalNative();
1718    private static native void stopHalNative();
1719    private static native void waitForHalEventNative();
1720
1721    private static class MonitorThread extends Thread {
1722        public void run() {
1723            Log.i(TAG, "Waiting for HAL events mWifiHalHandle=" + Long.toString(sWifiHalHandle));
1724            waitForHalEventNative();
1725        }
1726    }
1727
1728    public boolean startHal() {
1729        String debugLog = "startHal stack: ";
1730        java.lang.StackTraceElement[] elements = Thread.currentThread().getStackTrace();
1731        for (int i = 2; i < elements.length && i <= 7; i++ ) {
1732            debugLog = debugLog + " - " + elements[i].getMethodName();
1733        }
1734
1735        sLocalLog.log(debugLog);
1736
1737        synchronized (sLock) {
1738            if (startHalNative()) {
1739                int wlan0Index = queryInterfaceIndex(mInterfaceName);
1740                if (wlan0Index == -1) {
1741                    if (DBG) sLocalLog.log("Could not find interface with name: " + mInterfaceName);
1742                    return false;
1743                }
1744                sWlan0Index = wlan0Index;
1745                sThread = new MonitorThread();
1746                sThread.start();
1747                return true;
1748            } else {
1749                if (DBG) sLocalLog.log("Could not start hal");
1750                Log.e(TAG, "Could not start hal");
1751                return false;
1752            }
1753        }
1754    }
1755
1756    public void stopHal() {
1757        synchronized (sLock) {
1758            if (isHalStarted()) {
1759                stopHalNative();
1760                try {
1761                    sThread.join(STOP_HAL_TIMEOUT_MS);
1762                    Log.d(TAG, "HAL event thread stopped successfully");
1763                } catch (InterruptedException e) {
1764                    Log.e(TAG, "Could not stop HAL cleanly");
1765                }
1766                sThread = null;
1767                sWifiHalHandle = 0;
1768                sWifiIfaceHandles = null;
1769                sWlan0Index = -1;
1770            }
1771        }
1772    }
1773
1774    public boolean isHalStarted() {
1775        return (sWifiHalHandle != 0);
1776    }
1777    private static native int getInterfacesNative();
1778
1779    public int queryInterfaceIndex(String interfaceName) {
1780        synchronized (sLock) {
1781            if (isHalStarted()) {
1782                int num = getInterfacesNative();
1783                for (int i = 0; i < num; i++) {
1784                    String name = getInterfaceNameNative(i);
1785                    if (name.equals(interfaceName)) {
1786                        return i;
1787                    }
1788                }
1789            }
1790        }
1791        return -1;
1792    }
1793
1794    private static native String getInterfaceNameNative(int index);
1795    public String getInterfaceName(int index) {
1796        synchronized (sLock) {
1797            return getInterfaceNameNative(index);
1798        }
1799    }
1800
1801    // TODO: Change variable names to camel style.
1802    public static class ScanCapabilities {
1803        public int  max_scan_cache_size;
1804        public int  max_scan_buckets;
1805        public int  max_ap_cache_per_scan;
1806        public int  max_rssi_sample_size;
1807        public int  max_scan_reporting_threshold;
1808        public int  max_hotlist_bssids;
1809        public int  max_significant_wifi_change_aps;
1810        public int  max_bssid_history_entries;
1811        public int  max_number_epno_networks;
1812        public int  max_number_epno_networks_by_ssid;
1813        public int  max_number_of_white_listed_ssid;
1814    }
1815
1816    public boolean getScanCapabilities(ScanCapabilities capabilities) {
1817        synchronized (sLock) {
1818            return isHalStarted() && getScanCapabilitiesNative(sWlan0Index, capabilities);
1819        }
1820    }
1821
1822    private static native boolean getScanCapabilitiesNative(
1823            int iface, ScanCapabilities capabilities);
1824
1825    private static native boolean startScanNative(int iface, int id, ScanSettings settings);
1826    private static native boolean stopScanNative(int iface, int id);
1827    private static native WifiScanner.ScanData[] getScanResultsNative(int iface, boolean flush);
1828    private static native WifiLinkLayerStats getWifiLinkLayerStatsNative(int iface);
1829    private static native void setWifiLinkLayerStatsNative(int iface, int enable);
1830
1831    public static class ChannelSettings {
1832        public int frequency;
1833        public int dwell_time_ms;
1834        public boolean passive;
1835    }
1836
1837    public static class BucketSettings {
1838        public int bucket;
1839        public int band;
1840        public int period_ms;
1841        public int max_period_ms;
1842        public int step_count;
1843        public int report_events;
1844        public int num_channels;
1845        public ChannelSettings[] channels;
1846    }
1847
1848    public static class ScanSettings {
1849        public int base_period_ms;
1850        public int max_ap_per_scan;
1851        public int report_threshold_percent;
1852        public int report_threshold_num_scans;
1853        public int num_buckets;
1854        /* Not part of gscan HAL API. Used only for wpa_supplicant scanning */
1855        public int[] hiddenNetworkIds;
1856        public BucketSettings[] buckets;
1857    }
1858
1859    /**
1860     * Network parameters to start PNO scan.
1861     */
1862    public static class PnoNetwork {
1863        public String ssid;
1864        public int networkId;
1865        public int priority;
1866        public byte flags;
1867        public byte auth_bit_field;
1868    }
1869
1870    /**
1871     * Parameters to start PNO scan. This holds the list of networks which are going to used for
1872     * PNO scan.
1873     */
1874    public static class PnoSettings {
1875        public int min5GHzRssi;
1876        public int min24GHzRssi;
1877        public int initialScoreMax;
1878        public int currentConnectionBonus;
1879        public int sameNetworkBonus;
1880        public int secureBonus;
1881        public int band5GHzBonus;
1882        public boolean isConnected;
1883        public PnoNetwork[] networkList;
1884    }
1885
1886    /**
1887     * Wi-Fi channel information.
1888     */
1889    public static class WifiChannelInfo {
1890        int mPrimaryFrequency;
1891        int mCenterFrequency0;
1892        int mCenterFrequency1;
1893        int mChannelWidth;
1894        // TODO: add preamble once available in HAL.
1895    }
1896
1897    public static interface ScanEventHandler {
1898        /**
1899         * Called for each AP as it is found with the entire contents of the beacon/probe response.
1900         * Only called when WifiScanner.REPORT_EVENT_FULL_SCAN_RESULT is specified.
1901         */
1902        void onFullScanResult(ScanResult fullScanResult, int bucketsScanned);
1903        /**
1904         * Callback on an event during a gscan scan.
1905         * See WifiNative.WIFI_SCAN_* for possible values.
1906         */
1907        void onScanStatus(int event);
1908        /**
1909         * Called with the current cached scan results when gscan is paused.
1910         */
1911        void onScanPaused(WifiScanner.ScanData[] data);
1912        /**
1913         * Called with the current cached scan results when gscan is resumed.
1914         */
1915        void onScanRestarted();
1916    }
1917
1918    /**
1919     * Handler to notify the occurrence of various events during PNO scan.
1920     */
1921    public interface PnoEventHandler {
1922        /**
1923         * Callback to notify when one of the shortlisted networks is found during PNO scan.
1924         * @param results List of Scan results received.
1925         */
1926        void onPnoNetworkFound(ScanResult[] results);
1927
1928        /**
1929         * Callback to notify when the PNO scan schedule fails.
1930         */
1931        void onPnoScanFailed();
1932    }
1933
1934    /* scan status, keep these values in sync with gscan.h */
1935    public static final int WIFI_SCAN_RESULTS_AVAILABLE = 0;
1936    public static final int WIFI_SCAN_THRESHOLD_NUM_SCANS = 1;
1937    public static final int WIFI_SCAN_THRESHOLD_PERCENT = 2;
1938    public static final int WIFI_SCAN_FAILED = 3;
1939
1940    // Callback from native
1941    private static void onScanStatus(int id, int event) {
1942        ScanEventHandler handler = sScanEventHandler;
1943        if (handler != null) {
1944            handler.onScanStatus(event);
1945        }
1946    }
1947
1948    public static  WifiSsid createWifiSsid(byte[] rawSsid) {
1949        String ssidHexString = String.valueOf(HexEncoding.encode(rawSsid));
1950
1951        if (ssidHexString == null) {
1952            return null;
1953        }
1954
1955        WifiSsid wifiSsid = WifiSsid.createFromHex(ssidHexString);
1956
1957        return wifiSsid;
1958    }
1959
1960    public static String ssidConvert(byte[] rawSsid) {
1961        String ssid;
1962
1963        CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
1964        try {
1965            CharBuffer decoded = decoder.decode(ByteBuffer.wrap(rawSsid));
1966            ssid = decoded.toString();
1967        } catch (CharacterCodingException cce) {
1968            ssid = null;
1969        }
1970
1971        if (ssid == null) {
1972            ssid = new String(rawSsid, StandardCharsets.ISO_8859_1);
1973        }
1974
1975        return ssid;
1976    }
1977
1978    // Called from native
1979    public static boolean setSsid(byte[] rawSsid, ScanResult result) {
1980        if (rawSsid == null || rawSsid.length == 0 || result == null) {
1981            return false;
1982        }
1983
1984        result.SSID = ssidConvert(rawSsid);
1985        result.wifiSsid = createWifiSsid(rawSsid);
1986        return true;
1987    }
1988
1989    private static void populateScanResult(ScanResult result, int beaconCap, String dbg) {
1990        if (dbg == null) dbg = "";
1991
1992        InformationElementUtil.HtOperation htOperation = new InformationElementUtil.HtOperation();
1993        InformationElementUtil.VhtOperation vhtOperation =
1994                new InformationElementUtil.VhtOperation();
1995        InformationElementUtil.ExtendedCapabilities extendedCaps =
1996                new InformationElementUtil.ExtendedCapabilities();
1997
1998        ScanResult.InformationElement elements[] =
1999                InformationElementUtil.parseInformationElements(result.bytes);
2000        for (ScanResult.InformationElement ie : elements) {
2001            if(ie.id == ScanResult.InformationElement.EID_HT_OPERATION) {
2002                htOperation.from(ie);
2003            } else if(ie.id == ScanResult.InformationElement.EID_VHT_OPERATION) {
2004                vhtOperation.from(ie);
2005            } else if (ie.id == ScanResult.InformationElement.EID_EXTENDED_CAPS) {
2006                extendedCaps.from(ie);
2007            }
2008        }
2009
2010        if (extendedCaps.is80211McRTTResponder) {
2011            result.setFlag(ScanResult.FLAG_80211mc_RESPONDER);
2012        } else {
2013            result.clearFlag(ScanResult.FLAG_80211mc_RESPONDER);
2014        }
2015
2016        //handle RTT related information
2017        if (vhtOperation.isValid()) {
2018            result.channelWidth = vhtOperation.getChannelWidth();
2019            result.centerFreq0 = vhtOperation.getCenterFreq0();
2020            result.centerFreq1 = vhtOperation.getCenterFreq1();
2021        } else {
2022            result.channelWidth = htOperation.getChannelWidth();
2023            result.centerFreq0 = htOperation.getCenterFreq0(result.frequency);
2024            result.centerFreq1  = 0;
2025        }
2026
2027        // build capabilities string
2028        BitSet beaconCapBits = new BitSet(16);
2029        for (int i = 0; i < 16; i++) {
2030            if ((beaconCap & (1 << i)) != 0) {
2031                beaconCapBits.set(i);
2032            }
2033        }
2034        result.capabilities = InformationElementUtil.Capabilities.buildCapabilities(elements,
2035                                               beaconCapBits);
2036
2037        if(DBG) {
2038            Log.d(TAG, dbg + "SSID: " + result.SSID + " ChannelWidth is: " + result.channelWidth
2039                    + " PrimaryFreq: " + result.frequency + " mCenterfreq0: " + result.centerFreq0
2040                    + " mCenterfreq1: " + result.centerFreq1 + (extendedCaps.is80211McRTTResponder
2041                    ? "Support RTT reponder: " : "Do not support RTT responder")
2042                    + " Capabilities: " + result.capabilities);
2043        }
2044
2045        result.informationElements = elements;
2046    }
2047
2048    // Callback from native
2049    private static void onFullScanResult(int id, ScanResult result,
2050            int bucketsScanned, int beaconCap) {
2051        if (DBG) Log.i(TAG, "Got a full scan results event, ssid = " + result.SSID);
2052
2053        ScanEventHandler handler = sScanEventHandler;
2054        if (handler != null) {
2055            populateScanResult(result, beaconCap, " onFullScanResult ");
2056            handler.onFullScanResult(result, bucketsScanned);
2057        }
2058    }
2059
2060    private static int sScanCmdId = 0;
2061    private static ScanEventHandler sScanEventHandler;
2062    private static ScanSettings sScanSettings;
2063
2064    public boolean startScan(ScanSettings settings, ScanEventHandler eventHandler) {
2065        synchronized (sLock) {
2066            if (isHalStarted()) {
2067                if (sScanCmdId != 0) {
2068                    stopScan();
2069                } else if (sScanSettings != null || sScanEventHandler != null) {
2070                /* current scan is paused; no need to stop it */
2071                }
2072
2073                sScanCmdId = getNewCmdIdLocked();
2074
2075                sScanSettings = settings;
2076                sScanEventHandler = eventHandler;
2077
2078                if (startScanNative(sWlan0Index, sScanCmdId, settings) == false) {
2079                    sScanEventHandler = null;
2080                    sScanSettings = null;
2081                    sScanCmdId = 0;
2082                    return false;
2083                }
2084
2085                return true;
2086            } else {
2087                return false;
2088            }
2089        }
2090    }
2091
2092    public void stopScan() {
2093        synchronized (sLock) {
2094            if (isHalStarted()) {
2095                if (sScanCmdId != 0) {
2096                    stopScanNative(sWlan0Index, sScanCmdId);
2097                }
2098                sScanSettings = null;
2099                sScanEventHandler = null;
2100                sScanCmdId = 0;
2101            }
2102        }
2103    }
2104
2105    public void pauseScan() {
2106        synchronized (sLock) {
2107            if (isHalStarted()) {
2108                if (sScanCmdId != 0 && sScanSettings != null && sScanEventHandler != null) {
2109                    Log.d(TAG, "Pausing scan");
2110                    WifiScanner.ScanData scanData[] = getScanResultsNative(sWlan0Index, true);
2111                    stopScanNative(sWlan0Index, sScanCmdId);
2112                    sScanCmdId = 0;
2113                    sScanEventHandler.onScanPaused(scanData);
2114                }
2115            }
2116        }
2117    }
2118
2119    public void restartScan() {
2120        synchronized (sLock) {
2121            if (isHalStarted()) {
2122                if (sScanCmdId == 0 && sScanSettings != null && sScanEventHandler != null) {
2123                    Log.d(TAG, "Restarting scan");
2124                    ScanEventHandler handler = sScanEventHandler;
2125                    ScanSettings settings = sScanSettings;
2126                    if (startScan(sScanSettings, sScanEventHandler)) {
2127                        sScanEventHandler.onScanRestarted();
2128                    } else {
2129                    /* we are still paused; don't change state */
2130                        sScanEventHandler = handler;
2131                        sScanSettings = settings;
2132                    }
2133                }
2134            }
2135        }
2136    }
2137
2138    public WifiScanner.ScanData[] getScanResults(boolean flush) {
2139        synchronized (sLock) {
2140            WifiScanner.ScanData[] sd = null;
2141            if (isHalStarted()) {
2142                sd = getScanResultsNative(sWlan0Index, flush);
2143            }
2144
2145            if (sd != null) {
2146                return sd;
2147            } else {
2148                return new WifiScanner.ScanData[0];
2149            }
2150        }
2151    }
2152
2153    public static interface HotlistEventHandler {
2154        void onHotlistApFound (ScanResult[] result);
2155        void onHotlistApLost  (ScanResult[] result);
2156    }
2157
2158    private static int sHotlistCmdId = 0;
2159    private static HotlistEventHandler sHotlistEventHandler;
2160
2161    private native static boolean setHotlistNative(int iface, int id,
2162            WifiScanner.HotlistSettings settings);
2163    private native static boolean resetHotlistNative(int iface, int id);
2164
2165    public boolean setHotlist(WifiScanner.HotlistSettings settings,
2166            HotlistEventHandler eventHandler) {
2167        synchronized (sLock) {
2168            if (isHalStarted()) {
2169                if (sHotlistCmdId != 0) {
2170                    return false;
2171                } else {
2172                    sHotlistCmdId = getNewCmdIdLocked();
2173                }
2174
2175                sHotlistEventHandler = eventHandler;
2176                if (setHotlistNative(sWlan0Index, sHotlistCmdId, settings) == false) {
2177                    sHotlistEventHandler = null;
2178                    return false;
2179                }
2180
2181                return true;
2182            } else {
2183                return false;
2184            }
2185        }
2186    }
2187
2188    public void resetHotlist() {
2189        synchronized (sLock) {
2190            if (isHalStarted()) {
2191                if (sHotlistCmdId != 0) {
2192                    resetHotlistNative(sWlan0Index, sHotlistCmdId);
2193                    sHotlistCmdId = 0;
2194                    sHotlistEventHandler = null;
2195                }
2196            }
2197        }
2198    }
2199
2200    // Callback from native
2201    private static void onHotlistApFound(int id, ScanResult[] results) {
2202        HotlistEventHandler handler = sHotlistEventHandler;
2203        if (handler != null) {
2204            handler.onHotlistApFound(results);
2205        } else {
2206            /* this can happen because of race conditions */
2207            Log.d(TAG, "Ignoring hotlist AP found event");
2208        }
2209    }
2210
2211    // Callback from native
2212    private static void onHotlistApLost(int id, ScanResult[] results) {
2213        HotlistEventHandler handler = sHotlistEventHandler;
2214        if (handler != null) {
2215            handler.onHotlistApLost(results);
2216        } else {
2217            /* this can happen because of race conditions */
2218            Log.d(TAG, "Ignoring hotlist AP lost event");
2219        }
2220    }
2221
2222    public static interface SignificantWifiChangeEventHandler {
2223        void onChangesFound(ScanResult[] result);
2224    }
2225
2226    private static SignificantWifiChangeEventHandler sSignificantWifiChangeHandler;
2227    private static int sSignificantWifiChangeCmdId;
2228
2229    private static native boolean trackSignificantWifiChangeNative(
2230            int iface, int id, WifiScanner.WifiChangeSettings settings);
2231    private static native boolean untrackSignificantWifiChangeNative(int iface, int id);
2232
2233    public boolean trackSignificantWifiChange(
2234            WifiScanner.WifiChangeSettings settings, SignificantWifiChangeEventHandler handler) {
2235        synchronized (sLock) {
2236            if (isHalStarted()) {
2237                if (sSignificantWifiChangeCmdId != 0) {
2238                    return false;
2239                } else {
2240                    sSignificantWifiChangeCmdId = getNewCmdIdLocked();
2241                }
2242
2243                sSignificantWifiChangeHandler = handler;
2244                if (trackSignificantWifiChangeNative(sWlan0Index, sSignificantWifiChangeCmdId,
2245                        settings) == false) {
2246                    sSignificantWifiChangeHandler = null;
2247                    return false;
2248                }
2249
2250                return true;
2251            } else {
2252                return false;
2253            }
2254
2255        }
2256    }
2257
2258    public void untrackSignificantWifiChange() {
2259        synchronized (sLock) {
2260            if (isHalStarted()) {
2261                if (sSignificantWifiChangeCmdId != 0) {
2262                    untrackSignificantWifiChangeNative(sWlan0Index, sSignificantWifiChangeCmdId);
2263                    sSignificantWifiChangeCmdId = 0;
2264                    sSignificantWifiChangeHandler = null;
2265                }
2266            }
2267        }
2268    }
2269
2270    // Callback from native
2271    private static void onSignificantWifiChange(int id, ScanResult[] results) {
2272        SignificantWifiChangeEventHandler handler = sSignificantWifiChangeHandler;
2273        if (handler != null) {
2274            handler.onChangesFound(results);
2275        } else {
2276            /* this can happen because of race conditions */
2277            Log.d(TAG, "Ignoring significant wifi change");
2278        }
2279    }
2280
2281    public WifiLinkLayerStats getWifiLinkLayerStats(String iface) {
2282        // TODO: use correct iface name to Index translation
2283        if (iface == null) return null;
2284        synchronized (sLock) {
2285            if (isHalStarted()) {
2286                return getWifiLinkLayerStatsNative(sWlan0Index);
2287            } else {
2288                return null;
2289            }
2290        }
2291    }
2292
2293    public void setWifiLinkLayerStats(String iface, int enable) {
2294        if (iface == null) return;
2295        synchronized (sLock) {
2296            if (isHalStarted()) {
2297                setWifiLinkLayerStatsNative(sWlan0Index, enable);
2298            }
2299        }
2300    }
2301
2302    public static native int getSupportedFeatureSetNative(int iface);
2303    public int getSupportedFeatureSet() {
2304        synchronized (sLock) {
2305            if (isHalStarted()) {
2306                return getSupportedFeatureSetNative(sWlan0Index);
2307            } else {
2308                Log.d(TAG, "Failing getSupportedFeatureset because HAL isn't started");
2309                return 0;
2310            }
2311        }
2312    }
2313
2314    /* Rtt related commands/events */
2315    public static interface RttEventHandler {
2316        void onRttResults(RttManager.RttResult[] result);
2317    }
2318
2319    private static RttEventHandler sRttEventHandler;
2320    private static int sRttCmdId;
2321
2322    // Callback from native
2323    private static void onRttResults(int id, RttManager.RttResult[] results) {
2324        RttEventHandler handler = sRttEventHandler;
2325        if (handler != null && id == sRttCmdId) {
2326            Log.d(TAG, "Received " + results.length + " rtt results");
2327            handler.onRttResults(results);
2328            sRttCmdId = 0;
2329        } else {
2330            Log.d(TAG, "RTT Received event for unknown cmd = " + id +
2331                    ", current id = " + sRttCmdId);
2332        }
2333    }
2334
2335    private static native boolean requestRangeNative(
2336            int iface, int id, RttManager.RttParams[] params);
2337    private static native boolean cancelRangeRequestNative(
2338            int iface, int id, RttManager.RttParams[] params);
2339
2340    public boolean requestRtt(
2341            RttManager.RttParams[] params, RttEventHandler handler) {
2342        synchronized (sLock) {
2343            if (isHalStarted()) {
2344                if (sRttCmdId != 0) {
2345                    Log.v("TAG", "Last one is still under measurement!");
2346                    return false;
2347                } else {
2348                    sRttCmdId = getNewCmdIdLocked();
2349                }
2350                sRttEventHandler = handler;
2351                Log.v(TAG, "native issue RTT request");
2352                return requestRangeNative(sWlan0Index, sRttCmdId, params);
2353            } else {
2354                return false;
2355            }
2356        }
2357    }
2358
2359    public boolean cancelRtt(RttManager.RttParams[] params) {
2360        synchronized (sLock) {
2361            if (isHalStarted()) {
2362                if (sRttCmdId == 0) {
2363                    return false;
2364                }
2365
2366                sRttCmdId = 0;
2367
2368                if (cancelRangeRequestNative(sWlan0Index, sRttCmdId, params)) {
2369                    sRttEventHandler = null;
2370                    Log.v(TAG, "RTT cancel Request Successfully");
2371                    return true;
2372                } else {
2373                    Log.e(TAG, "RTT cancel Request failed");
2374                    return false;
2375                }
2376            } else {
2377                return false;
2378            }
2379        }
2380    }
2381
2382    private static int sRttResponderCmdId = 0;
2383
2384    private static native ResponderConfig enableRttResponderNative(int iface, int commandId,
2385            int timeoutSeconds, WifiChannelInfo channelHint);
2386    /**
2387     * Enable RTT responder role on the device. Returns {@link ResponderConfig} if the responder
2388     * role is successfully enabled, {@code null} otherwise.
2389     */
2390    @Nullable
2391    public ResponderConfig enableRttResponder(int timeoutSeconds) {
2392        synchronized (sLock) {
2393            if (!isHalStarted()) return null;
2394            if (sRttResponderCmdId != 0) {
2395                if (DBG) Log.e(mTAG, "responder mode already enabled - this shouldn't happen");
2396                return null;
2397            }
2398            int id = getNewCmdIdLocked();
2399            ResponderConfig config = enableRttResponderNative(
2400                    sWlan0Index, id, timeoutSeconds, null);
2401            if (config != null) sRttResponderCmdId = id;
2402            if (DBG) Log.d(TAG, "enabling rtt " + (config != null));
2403            return config;
2404        }
2405    }
2406
2407    private static native boolean disableRttResponderNative(int iface, int commandId);
2408    /**
2409     * Disable RTT responder role. Returns {@code true} if responder role is successfully disabled,
2410     * {@code false} otherwise.
2411     */
2412    public boolean disableRttResponder() {
2413        synchronized (sLock) {
2414            if (!isHalStarted()) return false;
2415            if (sRttResponderCmdId == 0) {
2416                Log.e(mTAG, "responder role not enabled yet");
2417                return true;
2418            }
2419            sRttResponderCmdId = 0;
2420            return disableRttResponderNative(sWlan0Index, sRttResponderCmdId);
2421        }
2422    }
2423
2424    private static native boolean setScanningMacOuiNative(int iface, byte[] oui);
2425
2426    public boolean setScanningMacOui(byte[] oui) {
2427        synchronized (sLock) {
2428            if (isHalStarted()) {
2429                return setScanningMacOuiNative(sWlan0Index, oui);
2430            } else {
2431                return false;
2432            }
2433        }
2434    }
2435
2436    private static native int[] getChannelsForBandNative(
2437            int iface, int band);
2438
2439    public int [] getChannelsForBand(int band) {
2440        synchronized (sLock) {
2441            if (isHalStarted()) {
2442                return getChannelsForBandNative(sWlan0Index, band);
2443            } else {
2444                return null;
2445            }
2446        }
2447    }
2448
2449    private static native boolean isGetChannelsForBandSupportedNative();
2450    public boolean isGetChannelsForBandSupported(){
2451        synchronized (sLock) {
2452            if (isHalStarted()) {
2453                return isGetChannelsForBandSupportedNative();
2454            } else {
2455                return false;
2456            }
2457        }
2458    }
2459
2460    private static native boolean setDfsFlagNative(int iface, boolean dfsOn);
2461    public boolean setDfsFlag(boolean dfsOn) {
2462        synchronized (sLock) {
2463            if (isHalStarted()) {
2464                return setDfsFlagNative(sWlan0Index, dfsOn);
2465            } else {
2466                return false;
2467            }
2468        }
2469    }
2470
2471    private static native boolean setInterfaceUpNative(boolean up);
2472    public boolean setInterfaceUp(boolean up) {
2473        synchronized (sLock) {
2474            if (isHalStarted()) {
2475                return setInterfaceUpNative(up);
2476            } else {
2477                return false;
2478            }
2479        }
2480    }
2481
2482    private static native RttManager.RttCapabilities getRttCapabilitiesNative(int iface);
2483    public RttManager.RttCapabilities getRttCapabilities() {
2484        synchronized (sLock) {
2485            if (isHalStarted()) {
2486                return getRttCapabilitiesNative(sWlan0Index);
2487            } else {
2488                return null;
2489            }
2490        }
2491    }
2492
2493    private static native ApfCapabilities getApfCapabilitiesNative(int iface);
2494    public ApfCapabilities getApfCapabilities() {
2495        synchronized (sLock) {
2496            if (isHalStarted()) {
2497                return getApfCapabilitiesNative(sWlan0Index);
2498            } else {
2499                return null;
2500            }
2501        }
2502    }
2503
2504    private static native boolean installPacketFilterNative(int iface, byte[] filter);
2505    public boolean installPacketFilter(byte[] filter) {
2506        synchronized (sLock) {
2507            if (isHalStarted()) {
2508                return installPacketFilterNative(sWlan0Index, filter);
2509            } else {
2510                return false;
2511            }
2512        }
2513    }
2514
2515    private static native boolean setCountryCodeHalNative(int iface, String CountryCode);
2516    public boolean setCountryCodeHal(String CountryCode) {
2517        synchronized (sLock) {
2518            if (isHalStarted()) {
2519                return setCountryCodeHalNative(sWlan0Index, CountryCode);
2520            } else {
2521                return false;
2522            }
2523        }
2524    }
2525
2526    /* Rtt related commands/events */
2527    public abstract class TdlsEventHandler {
2528        abstract public void onTdlsStatus(String macAddr, int status, int reason);
2529    }
2530
2531    private static TdlsEventHandler sTdlsEventHandler;
2532
2533    private static native boolean enableDisableTdlsNative(int iface, boolean enable,
2534            String macAddr);
2535    public boolean enableDisableTdls(boolean enable, String macAdd, TdlsEventHandler tdlsCallBack) {
2536        synchronized (sLock) {
2537            sTdlsEventHandler = tdlsCallBack;
2538            return enableDisableTdlsNative(sWlan0Index, enable, macAdd);
2539        }
2540    }
2541
2542    // Once TDLS per mac and event feature is implemented, this class definition should be
2543    // moved to the right place, like WifiManager etc
2544    public static class TdlsStatus {
2545        int channel;
2546        int global_operating_class;
2547        int state;
2548        int reason;
2549    }
2550    private static native TdlsStatus getTdlsStatusNative(int iface, String macAddr);
2551    public TdlsStatus getTdlsStatus(String macAdd) {
2552        synchronized (sLock) {
2553            if (isHalStarted()) {
2554                return getTdlsStatusNative(sWlan0Index, macAdd);
2555            } else {
2556                return null;
2557            }
2558        }
2559    }
2560
2561    //ToFix: Once TDLS per mac and event feature is implemented, this class definition should be
2562    // moved to the right place, like WifiStateMachine etc
2563    public static class TdlsCapabilities {
2564        /* Maximum TDLS session number can be supported by the Firmware and hardware */
2565        int maxConcurrentTdlsSessionNumber;
2566        boolean isGlobalTdlsSupported;
2567        boolean isPerMacTdlsSupported;
2568        boolean isOffChannelTdlsSupported;
2569    }
2570
2571
2572
2573    private static native TdlsCapabilities getTdlsCapabilitiesNative(int iface);
2574    public TdlsCapabilities getTdlsCapabilities () {
2575        synchronized (sLock) {
2576            if (isHalStarted()) {
2577                return getTdlsCapabilitiesNative(sWlan0Index);
2578            } else {
2579                return null;
2580            }
2581        }
2582    }
2583
2584    private static boolean onTdlsStatus(String macAddr, int status, int reason) {
2585        TdlsEventHandler handler = sTdlsEventHandler;
2586        if (handler == null) {
2587            return false;
2588        } else {
2589            handler.onTdlsStatus(macAddr, status, reason);
2590            return true;
2591        }
2592    }
2593
2594    //---------------------------------------------------------------------------------
2595
2596    /* Wifi Logger commands/events */
2597
2598    public static interface WifiLoggerEventHandler {
2599        void onRingBufferData(RingBufferStatus status, byte[] buffer);
2600        void onWifiAlert(int errorCode, byte[] buffer);
2601    }
2602
2603    private static WifiLoggerEventHandler sWifiLoggerEventHandler = null;
2604
2605    // Callback from native
2606    private static void onRingBufferData(RingBufferStatus status, byte[] buffer) {
2607        WifiLoggerEventHandler handler = sWifiLoggerEventHandler;
2608        if (handler != null)
2609            handler.onRingBufferData(status, buffer);
2610    }
2611
2612    // Callback from native
2613    private static void onWifiAlert(byte[] buffer, int errorCode) {
2614        WifiLoggerEventHandler handler = sWifiLoggerEventHandler;
2615        if (handler != null)
2616            handler.onWifiAlert(errorCode, buffer);
2617    }
2618
2619    private static int sLogCmdId = -1;
2620    private static native boolean setLoggingEventHandlerNative(int iface, int id);
2621    public boolean setLoggingEventHandler(WifiLoggerEventHandler handler) {
2622        synchronized (sLock) {
2623            if (isHalStarted()) {
2624                int oldId =  sLogCmdId;
2625                sLogCmdId = getNewCmdIdLocked();
2626                if (!setLoggingEventHandlerNative(sWlan0Index, sLogCmdId)) {
2627                    sLogCmdId = oldId;
2628                    return false;
2629                }
2630                sWifiLoggerEventHandler = handler;
2631                return true;
2632            } else {
2633                return false;
2634            }
2635        }
2636    }
2637
2638    private static native boolean startLoggingRingBufferNative(int iface, int verboseLevel,
2639            int flags, int minIntervalSec ,int minDataSize, String ringName);
2640    public boolean startLoggingRingBuffer(int verboseLevel, int flags, int maxInterval,
2641            int minDataSize, String ringName){
2642        synchronized (sLock) {
2643            if (isHalStarted()) {
2644                return startLoggingRingBufferNative(sWlan0Index, verboseLevel, flags, maxInterval,
2645                        minDataSize, ringName);
2646            } else {
2647                return false;
2648            }
2649        }
2650    }
2651
2652    private static native int getSupportedLoggerFeatureSetNative(int iface);
2653    public int getSupportedLoggerFeatureSet() {
2654        synchronized (sLock) {
2655            if (isHalStarted()) {
2656                return getSupportedLoggerFeatureSetNative(sWlan0Index);
2657            } else {
2658                return 0;
2659            }
2660        }
2661    }
2662
2663    private static native boolean resetLogHandlerNative(int iface, int id);
2664    public boolean resetLogHandler() {
2665        synchronized (sLock) {
2666            if (isHalStarted()) {
2667                if (sLogCmdId == -1) {
2668                    Log.e(TAG,"Can not reset handler Before set any handler");
2669                    return false;
2670                }
2671                sWifiLoggerEventHandler = null;
2672                if (resetLogHandlerNative(sWlan0Index, sLogCmdId)) {
2673                    sLogCmdId = -1;
2674                    return true;
2675                } else {
2676                    return false;
2677                }
2678            } else {
2679                return false;
2680            }
2681        }
2682    }
2683
2684    private static native String getDriverVersionNative(int iface);
2685    public String getDriverVersion() {
2686        synchronized (sLock) {
2687            if (isHalStarted()) {
2688                return getDriverVersionNative(sWlan0Index);
2689            } else {
2690                return "";
2691            }
2692        }
2693    }
2694
2695
2696    private static native String getFirmwareVersionNative(int iface);
2697    public String getFirmwareVersion() {
2698        synchronized (sLock) {
2699            if (isHalStarted()) {
2700                return getFirmwareVersionNative(sWlan0Index);
2701            } else {
2702                return "";
2703            }
2704        }
2705    }
2706
2707    public static class RingBufferStatus{
2708        String name;
2709        int flag;
2710        int ringBufferId;
2711        int ringBufferByteSize;
2712        int verboseLevel;
2713        int writtenBytes;
2714        int readBytes;
2715        int writtenRecords;
2716
2717        @Override
2718        public String toString() {
2719            return "name: " + name + " flag: " + flag + " ringBufferId: " + ringBufferId +
2720                    " ringBufferByteSize: " +ringBufferByteSize + " verboseLevel: " +verboseLevel +
2721                    " writtenBytes: " + writtenBytes + " readBytes: " + readBytes +
2722                    " writtenRecords: " + writtenRecords;
2723        }
2724    }
2725
2726    private static native RingBufferStatus[] getRingBufferStatusNative(int iface);
2727    public RingBufferStatus[] getRingBufferStatus() {
2728        synchronized (sLock) {
2729            if (isHalStarted()) {
2730                return getRingBufferStatusNative(sWlan0Index);
2731            } else {
2732                return null;
2733            }
2734        }
2735    }
2736
2737    private static native boolean getRingBufferDataNative(int iface, String ringName);
2738    public boolean getRingBufferData(String ringName) {
2739        synchronized (sLock) {
2740            if (isHalStarted()) {
2741                return getRingBufferDataNative(sWlan0Index, ringName);
2742            } else {
2743                return false;
2744            }
2745        }
2746    }
2747
2748    private static byte[] mFwMemoryDump;
2749    // Callback from native
2750    private static void onWifiFwMemoryAvailable(byte[] buffer) {
2751        mFwMemoryDump = buffer;
2752        if (DBG) {
2753            Log.d(TAG, "onWifiFwMemoryAvailable is called and buffer length is: " +
2754                    (buffer == null ? 0 :  buffer.length));
2755        }
2756    }
2757
2758    private static native boolean getFwMemoryDumpNative(int iface);
2759    public byte[] getFwMemoryDump() {
2760        synchronized (sLock) {
2761            if (isHalStarted()) {
2762                if(getFwMemoryDumpNative(sWlan0Index)) {
2763                    byte[] fwMemoryDump = mFwMemoryDump;
2764                    mFwMemoryDump = null;
2765                    return fwMemoryDump;
2766                } else {
2767                    return null;
2768                }
2769            }
2770            return null;
2771        }
2772    }
2773
2774    //---------------------------------------------------------------------------------
2775    /* Configure ePNO/PNO */
2776    private static PnoEventHandler sPnoEventHandler;
2777    private static int sPnoCmdId = 0;
2778
2779    private static native boolean setPnoListNative(int iface, int id, PnoSettings settings);
2780
2781    /**
2782     * Set the PNO settings & the network list in HAL to start PNO.
2783     * @param settings PNO settings and network list.
2784     * @param eventHandler Handler to receive notifications back during PNO scan.
2785     * @return true if success, false otherwise
2786     */
2787    public boolean setPnoList(PnoSettings settings, PnoEventHandler eventHandler) {
2788        Log.e(TAG, "setPnoList cmd " + sPnoCmdId);
2789
2790        synchronized (sLock) {
2791            if (isHalStarted()) {
2792                sPnoCmdId = getNewCmdIdLocked();
2793                sPnoEventHandler = eventHandler;
2794                if (setPnoListNative(sWlan0Index, sPnoCmdId, settings)) {
2795                    return true;
2796                }
2797            }
2798            sPnoEventHandler = null;
2799            return false;
2800        }
2801    }
2802
2803    /**
2804     * Set the PNO network list in HAL to start PNO.
2805     * @param list PNO network list.
2806     * @param eventHandler Handler to receive notifications back during PNO scan.
2807     * @return true if success, false otherwise
2808     */
2809    public boolean setPnoList(PnoNetwork[] list, PnoEventHandler eventHandler) {
2810        PnoSettings settings = new PnoSettings();
2811        settings.networkList = list;
2812        return setPnoList(settings, eventHandler);
2813    }
2814
2815    private static native boolean resetPnoListNative(int iface, int id);
2816
2817    /**
2818     * Reset the PNO settings in HAL to stop PNO.
2819     * @return true if success, false otherwise
2820     */
2821    public boolean resetPnoList() {
2822        Log.e(TAG, "resetPnoList cmd " + sPnoCmdId);
2823
2824        synchronized (sLock) {
2825            if (isHalStarted()) {
2826                sPnoCmdId = getNewCmdIdLocked();
2827                sPnoEventHandler = null;
2828                if (resetPnoListNative(sWlan0Index, sPnoCmdId)) {
2829                    return true;
2830                }
2831            }
2832            return false;
2833        }
2834    }
2835
2836    // Callback from native
2837    private static void onPnoNetworkFound(int id, ScanResult[] results, int[] beaconCaps) {
2838        if (results == null) {
2839            Log.e(TAG, "onPnoNetworkFound null results");
2840            return;
2841
2842        }
2843        Log.d(TAG, "WifiNative.onPnoNetworkFound result " + results.length);
2844
2845        PnoEventHandler handler = sPnoEventHandler;
2846        if (sPnoCmdId != 0 && handler != null) {
2847            for (int i=0; i<results.length; i++) {
2848                Log.e(TAG, "onPnoNetworkFound SSID " + results[i].SSID
2849                        + " " + results[i].level + " " + results[i].frequency);
2850
2851                populateScanResult(results[i], beaconCaps[i], "onPnoNetworkFound ");
2852                results[i].wifiSsid = WifiSsid.createFromAsciiEncoded(results[i].SSID);
2853            }
2854
2855            handler.onPnoNetworkFound(results);
2856        } else {
2857            /* this can happen because of race conditions */
2858            Log.d(TAG, "Ignoring Pno Network found event");
2859        }
2860    }
2861
2862    private native static boolean setBssidBlacklistNative(int iface, int id,
2863                                              String list[]);
2864
2865    public boolean setBssidBlacklist(String list[]) {
2866        int size = 0;
2867        if (list != null) {
2868            size = list.length;
2869        }
2870        Log.e(TAG, "setBssidBlacklist cmd " + sPnoCmdId + " size " + size);
2871
2872        synchronized (sLock) {
2873            if (isHalStarted()) {
2874                sPnoCmdId = getNewCmdIdLocked();
2875                return setBssidBlacklistNative(sWlan0Index, sPnoCmdId, list);
2876            } else {
2877                return false;
2878            }
2879        }
2880    }
2881
2882    private native static int startSendingOffloadedPacketNative(int iface, int idx,
2883                                    byte[] srcMac, byte[] dstMac, byte[] pktData, int period);
2884
2885    public int
2886    startSendingOffloadedPacket(int slot, KeepalivePacketData keepAlivePacket, int period) {
2887        Log.d(TAG, "startSendingOffloadedPacket slot=" + slot + " period=" + period);
2888
2889        String[] macAddrStr = getMacAddress().split(":");
2890        byte[] srcMac = new byte[6];
2891        for(int i = 0; i < 6; i++) {
2892            Integer hexVal = Integer.parseInt(macAddrStr[i], 16);
2893            srcMac[i] = hexVal.byteValue();
2894        }
2895        synchronized (sLock) {
2896            if (isHalStarted()) {
2897                return startSendingOffloadedPacketNative(sWlan0Index, slot, srcMac,
2898                        keepAlivePacket.dstMac, keepAlivePacket.data, period);
2899            } else {
2900                return -1;
2901            }
2902        }
2903    }
2904
2905    private native static int stopSendingOffloadedPacketNative(int iface, int idx);
2906
2907    public int
2908    stopSendingOffloadedPacket(int slot) {
2909        Log.d(TAG, "stopSendingOffloadedPacket " + slot);
2910        synchronized (sLock) {
2911            if (isHalStarted()) {
2912                return stopSendingOffloadedPacketNative(sWlan0Index, slot);
2913            } else {
2914                return -1;
2915            }
2916        }
2917    }
2918
2919    public static interface WifiRssiEventHandler {
2920        void onRssiThresholdBreached(byte curRssi);
2921    }
2922
2923    private static WifiRssiEventHandler sWifiRssiEventHandler;
2924
2925    // Callback from native
2926    private static void onRssiThresholdBreached(int id, byte curRssi) {
2927        WifiRssiEventHandler handler = sWifiRssiEventHandler;
2928        if (handler != null) {
2929            handler.onRssiThresholdBreached(curRssi);
2930        }
2931    }
2932
2933    private native static int startRssiMonitoringNative(int iface, int id,
2934                                        byte maxRssi, byte minRssi);
2935
2936    private static int sRssiMonitorCmdId = 0;
2937
2938    public int startRssiMonitoring(byte maxRssi, byte minRssi,
2939                                                WifiRssiEventHandler rssiEventHandler) {
2940        Log.d(TAG, "startRssiMonitoring: maxRssi=" + maxRssi + " minRssi=" + minRssi);
2941        synchronized (sLock) {
2942            sWifiRssiEventHandler = rssiEventHandler;
2943            if (isHalStarted()) {
2944                if (sRssiMonitorCmdId != 0) {
2945                    stopRssiMonitoring();
2946                }
2947
2948                sRssiMonitorCmdId = getNewCmdIdLocked();
2949                Log.d(TAG, "sRssiMonitorCmdId = " + sRssiMonitorCmdId);
2950                int ret = startRssiMonitoringNative(sWlan0Index, sRssiMonitorCmdId,
2951                        maxRssi, minRssi);
2952                if (ret != 0) { // if not success
2953                    sRssiMonitorCmdId = 0;
2954                }
2955                return ret;
2956            } else {
2957                return -1;
2958            }
2959        }
2960    }
2961
2962    private native static int stopRssiMonitoringNative(int iface, int idx);
2963
2964    public int stopRssiMonitoring() {
2965        Log.d(TAG, "stopRssiMonitoring, cmdId " + sRssiMonitorCmdId);
2966        synchronized (sLock) {
2967            if (isHalStarted()) {
2968                int ret = 0;
2969                if (sRssiMonitorCmdId != 0) {
2970                    ret = stopRssiMonitoringNative(sWlan0Index, sRssiMonitorCmdId);
2971                }
2972                sRssiMonitorCmdId = 0;
2973                return ret;
2974            } else {
2975                return -1;
2976            }
2977        }
2978    }
2979
2980    private static native WifiWakeReasonAndCounts getWlanWakeReasonCountNative(int iface);
2981
2982    /**
2983     * Fetch the host wakeup reasons stats from wlan driver.
2984     * @return the |WifiWakeReasonAndCounts| object retrieved from the wlan driver.
2985     */
2986    public WifiWakeReasonAndCounts getWlanWakeReasonCount() {
2987        Log.d(TAG, "getWlanWakeReasonCount " + sWlan0Index);
2988        synchronized (sLock) {
2989            if (isHalStarted()) {
2990                return getWlanWakeReasonCountNative(sWlan0Index);
2991            } else {
2992                return null;
2993            }
2994        }
2995    }
2996
2997    private static native int configureNeighborDiscoveryOffload(int iface, boolean enabled);
2998
2999    public boolean configureNeighborDiscoveryOffload(boolean enabled) {
3000        final String logMsg =  "configureNeighborDiscoveryOffload(" + enabled + ")";
3001        Log.d(mTAG, logMsg);
3002        synchronized (sLock) {
3003            if (isHalStarted()) {
3004                final int ret = configureNeighborDiscoveryOffload(sWlan0Index, enabled);
3005                if (ret != 0) {
3006                    Log.d(mTAG, logMsg + " returned: " + ret);
3007                }
3008                return (ret == 0);
3009            }
3010        }
3011        return false;
3012    }
3013}
3014