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